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         
2548         
2549         // remove Card from items.
2550         
2551        
2552         if (this.items.length) {
2553             var nitems = [];
2554             //Roo.log([info.items_n, info.position, this.items.length]);
2555             for (var i =0; i < this.items.length; i++) {
2556                 if (i == to_items_n && position == 'above') {
2557                     nitems.push(move_card);
2558                 }
2559                 nitems.push(this.items[i]);
2560                 if (i == to_items_n && position == 'below') {
2561                     nitems.push(move_card);
2562                 }
2563             }
2564             this.items = nitems;
2565             Roo.log(this.items);
2566         } else {
2567             this.items.push(move_card);
2568         }
2569         
2570         move_card.parentId = this.id;
2571         
2572         return true;
2573         
2574         
2575     },
2576     removeCard : function(c)
2577     {
2578         this.items = this.items.filter(function(e) { return e != c });
2579  
2580         var dom = c.el.dom;
2581         dom.parentNode.removeChild(dom);
2582         dom.style.width = ''; // clear with - which is set by drag.
2583         c.parentId = false;
2584         
2585     },
2586     
2587     /**    Decide whether to drop above or below a View node. */
2588     getDropPoint : function(e, n, dd)
2589     {
2590         if (dd) {
2591              return false;
2592         }
2593         if (n == this.containerEl.dom) {
2594             return "above";
2595         }
2596         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2597         var c = t + (b - t) / 2;
2598         var y = Roo.lib.Event.getPageY(e);
2599         if(y <= c) {
2600             return "above";
2601         }else{
2602             return "below";
2603         }
2604     },
2605     onToggleCollapse : function(e)
2606         {
2607         if (this.collapsed) {
2608             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2609             this.collapsableEl.addClass('show');
2610             this.collapsed = false;
2611             return;
2612         }
2613         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2614         this.collapsableEl.removeClass('show');
2615         this.collapsed = true;
2616         
2617     
2618     },
2619     
2620     onToggleRotate : function(e)
2621     {
2622         this.collapsableEl.removeClass('show');
2623         this.footerEl.removeClass('d-none');
2624         this.el.removeClass('roo-card-rotated');
2625         this.el.removeClass('d-none');
2626         if (this.rotated) {
2627             
2628             this.collapsableEl.addClass('show');
2629             this.rotated = false;
2630             this.fireEvent('rotate', this, this.rotated);
2631             return;
2632         }
2633         this.el.addClass('roo-card-rotated');
2634         this.footerEl.addClass('d-none');
2635         this.el.select('.roo-collapsable').removeClass('show');
2636         
2637         this.rotated = true;
2638         this.fireEvent('rotate', this, this.rotated);
2639     
2640     },
2641     
2642     dropPlaceHolder: function (action, info, data)
2643     {
2644         if (this.dropEl === false) {
2645             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2646             cls : 'd-none'
2647             },true);
2648         }
2649         this.dropEl.removeClass(['d-none', 'd-block']);        
2650         if (action == 'hide') {
2651             
2652             this.dropEl.addClass('d-none');
2653             return;
2654         }
2655         // FIXME - info.card == true!!!
2656         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2657         
2658         if (info.card !== true) {
2659             var cardel = info.card.el.dom;
2660             
2661             if (info.position == 'above') {
2662                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2663             } else if (cardel.nextSibling) {
2664                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2665             } else {
2666                 cardel.parentNode.append(this.dropEl.dom);
2667             }
2668         } else {
2669             // card container???
2670             this.containerEl.dom.append(this.dropEl.dom);
2671         }
2672         
2673         this.dropEl.addClass('d-block roo-card-dropzone');
2674         
2675         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2676         
2677         
2678     
2679     
2680     
2681     },
2682     setHeaderText: function(html)
2683     {
2684         this.headerContainerEl.dom.innerHTML = html;
2685     }
2686
2687     
2688 });
2689
2690 /*
2691  * - LGPL
2692  *
2693  * Card header - holder for the card header elements.
2694  * 
2695  */
2696
2697 /**
2698  * @class Roo.bootstrap.CardHeader
2699  * @extends Roo.bootstrap.Element
2700  * Bootstrap CardHeader class
2701  * @constructor
2702  * Create a new Card Header - that you can embed children into
2703  * @param {Object} config The config object
2704  */
2705
2706 Roo.bootstrap.CardHeader = function(config){
2707     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2708 };
2709
2710 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2711     
2712     
2713     container_method : 'getCardHeader' 
2714     
2715      
2716     
2717     
2718    
2719 });
2720
2721  
2722
2723  /*
2724  * - LGPL
2725  *
2726  * Card footer - holder for the card footer elements.
2727  * 
2728  */
2729
2730 /**
2731  * @class Roo.bootstrap.CardFooter
2732  * @extends Roo.bootstrap.Element
2733  * Bootstrap CardFooter class
2734  * @constructor
2735  * Create a new Card Footer - that you can embed children into
2736  * @param {Object} config The config object
2737  */
2738
2739 Roo.bootstrap.CardFooter = function(config){
2740     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2741 };
2742
2743 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2744     
2745     
2746     container_method : 'getCardFooter' 
2747     
2748      
2749     
2750     
2751    
2752 });
2753
2754  
2755
2756  /*
2757  * - LGPL
2758  *
2759  * Card header - holder for the card header elements.
2760  * 
2761  */
2762
2763 /**
2764  * @class Roo.bootstrap.CardImageTop
2765  * @extends Roo.bootstrap.Element
2766  * Bootstrap CardImageTop class
2767  * @constructor
2768  * Create a new Card Image Top container
2769  * @param {Object} config The config object
2770  */
2771
2772 Roo.bootstrap.CardImageTop = function(config){
2773     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2774 };
2775
2776 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2777     
2778    
2779     container_method : 'getCardImageTop' 
2780     
2781      
2782     
2783    
2784 });
2785
2786  
2787
2788  /*
2789  * - LGPL
2790  *
2791  * image
2792  * 
2793  */
2794
2795
2796 /**
2797  * @class Roo.bootstrap.Img
2798  * @extends Roo.bootstrap.Component
2799  * Bootstrap Img class
2800  * @cfg {Boolean} imgResponsive false | true
2801  * @cfg {String} border rounded | circle | thumbnail
2802  * @cfg {String} src image source
2803  * @cfg {String} alt image alternative text
2804  * @cfg {String} href a tag href
2805  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2806  * @cfg {String} xsUrl xs image source
2807  * @cfg {String} smUrl sm image source
2808  * @cfg {String} mdUrl md image source
2809  * @cfg {String} lgUrl lg image source
2810  * 
2811  * @constructor
2812  * Create a new Input
2813  * @param {Object} config The config object
2814  */
2815
2816 Roo.bootstrap.Img = function(config){
2817     Roo.bootstrap.Img.superclass.constructor.call(this, config);
2818     
2819     this.addEvents({
2820         // img events
2821         /**
2822          * @event click
2823          * The img click event for the img.
2824          * @param {Roo.EventObject} e
2825          */
2826         "click" : true
2827     });
2828 };
2829
2830 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
2831     
2832     imgResponsive: true,
2833     border: '',
2834     src: 'about:blank',
2835     href: false,
2836     target: false,
2837     xsUrl: '',
2838     smUrl: '',
2839     mdUrl: '',
2840     lgUrl: '',
2841
2842     getAutoCreate : function()
2843     {   
2844         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2845             return this.createSingleImg();
2846         }
2847         
2848         var cfg = {
2849             tag: 'div',
2850             cls: 'roo-image-responsive-group',
2851             cn: []
2852         };
2853         var _this = this;
2854         
2855         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2856             
2857             if(!_this[size + 'Url']){
2858                 return;
2859             }
2860             
2861             var img = {
2862                 tag: 'img',
2863                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2864                 html: _this.html || cfg.html,
2865                 src: _this[size + 'Url']
2866             };
2867             
2868             img.cls += ' roo-image-responsive-' + size;
2869             
2870             var s = ['xs', 'sm', 'md', 'lg'];
2871             
2872             s.splice(s.indexOf(size), 1);
2873             
2874             Roo.each(s, function(ss){
2875                 img.cls += ' hidden-' + ss;
2876             });
2877             
2878             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2879                 cfg.cls += ' img-' + _this.border;
2880             }
2881             
2882             if(_this.alt){
2883                 cfg.alt = _this.alt;
2884             }
2885             
2886             if(_this.href){
2887                 var a = {
2888                     tag: 'a',
2889                     href: _this.href,
2890                     cn: [
2891                         img
2892                     ]
2893                 };
2894
2895                 if(this.target){
2896                     a.target = _this.target;
2897                 }
2898             }
2899             
2900             cfg.cn.push((_this.href) ? a : img);
2901             
2902         });
2903         
2904         return cfg;
2905     },
2906     
2907     createSingleImg : function()
2908     {
2909         var cfg = {
2910             tag: 'img',
2911             cls: (this.imgResponsive) ? 'img-responsive' : '',
2912             html : null,
2913             src : 'about:blank'  // just incase src get's set to undefined?!?
2914         };
2915         
2916         cfg.html = this.html || cfg.html;
2917         
2918         cfg.src = this.src || cfg.src;
2919         
2920         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2921             cfg.cls += ' img-' + this.border;
2922         }
2923         
2924         if(this.alt){
2925             cfg.alt = this.alt;
2926         }
2927         
2928         if(this.href){
2929             var a = {
2930                 tag: 'a',
2931                 href: this.href,
2932                 cn: [
2933                     cfg
2934                 ]
2935             };
2936             
2937             if(this.target){
2938                 a.target = this.target;
2939             }
2940             
2941         }
2942         
2943         return (this.href) ? a : cfg;
2944     },
2945     
2946     initEvents: function() 
2947     {
2948         if(!this.href){
2949             this.el.on('click', this.onClick, this);
2950         }
2951         
2952     },
2953     
2954     onClick : function(e)
2955     {
2956         Roo.log('img onclick');
2957         this.fireEvent('click', this, e);
2958     },
2959     /**
2960      * Sets the url of the image - used to update it
2961      * @param {String} url the url of the image
2962      */
2963     
2964     setSrc : function(url)
2965     {
2966         this.src =  url;
2967         
2968         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2969             this.el.dom.src =  url;
2970             return;
2971         }
2972         
2973         this.el.select('img', true).first().dom.src =  url;
2974     }
2975     
2976     
2977    
2978 });
2979
2980  /*
2981  * - LGPL
2982  *
2983  * image
2984  * 
2985  */
2986
2987
2988 /**
2989  * @class Roo.bootstrap.Link
2990  * @extends Roo.bootstrap.Component
2991  * Bootstrap Link Class
2992  * @cfg {String} alt image alternative text
2993  * @cfg {String} href a tag href
2994  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2995  * @cfg {String} html the content of the link.
2996  * @cfg {String} anchor name for the anchor link
2997  * @cfg {String} fa - favicon
2998
2999  * @cfg {Boolean} preventDefault (true | false) default false
3000
3001  * 
3002  * @constructor
3003  * Create a new Input
3004  * @param {Object} config The config object
3005  */
3006
3007 Roo.bootstrap.Link = function(config){
3008     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3009     
3010     this.addEvents({
3011         // img events
3012         /**
3013          * @event click
3014          * The img click event for the img.
3015          * @param {Roo.EventObject} e
3016          */
3017         "click" : true
3018     });
3019 };
3020
3021 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3022     
3023     href: false,
3024     target: false,
3025     preventDefault: false,
3026     anchor : false,
3027     alt : false,
3028     fa: false,
3029
3030
3031     getAutoCreate : function()
3032     {
3033         var html = this.html || '';
3034         
3035         if (this.fa !== false) {
3036             html = '<i class="fa fa-' + this.fa + '"></i>';
3037         }
3038         var cfg = {
3039             tag: 'a'
3040         };
3041         // anchor's do not require html/href...
3042         if (this.anchor === false) {
3043             cfg.html = html;
3044             cfg.href = this.href || '#';
3045         } else {
3046             cfg.name = this.anchor;
3047             if (this.html !== false || this.fa !== false) {
3048                 cfg.html = html;
3049             }
3050             if (this.href !== false) {
3051                 cfg.href = this.href;
3052             }
3053         }
3054         
3055         if(this.alt !== false){
3056             cfg.alt = this.alt;
3057         }
3058         
3059         
3060         if(this.target !== false) {
3061             cfg.target = this.target;
3062         }
3063         
3064         return cfg;
3065     },
3066     
3067     initEvents: function() {
3068         
3069         if(!this.href || this.preventDefault){
3070             this.el.on('click', this.onClick, this);
3071         }
3072     },
3073     
3074     onClick : function(e)
3075     {
3076         if(this.preventDefault){
3077             e.preventDefault();
3078         }
3079         //Roo.log('img onclick');
3080         this.fireEvent('click', this, e);
3081     }
3082    
3083 });
3084
3085  /*
3086  * - LGPL
3087  *
3088  * header
3089  * 
3090  */
3091
3092 /**
3093  * @class Roo.bootstrap.Header
3094  * @extends Roo.bootstrap.Component
3095  * Bootstrap Header class
3096  * @cfg {String} html content of header
3097  * @cfg {Number} level (1|2|3|4|5|6) default 1
3098  * 
3099  * @constructor
3100  * Create a new Header
3101  * @param {Object} config The config object
3102  */
3103
3104
3105 Roo.bootstrap.Header  = function(config){
3106     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3107 };
3108
3109 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3110     
3111     //href : false,
3112     html : false,
3113     level : 1,
3114     
3115     
3116     
3117     getAutoCreate : function(){
3118         
3119         
3120         
3121         var cfg = {
3122             tag: 'h' + (1 *this.level),
3123             html: this.html || ''
3124         } ;
3125         
3126         return cfg;
3127     }
3128    
3129 });
3130
3131  
3132
3133  /*
3134  * Based on:
3135  * Ext JS Library 1.1.1
3136  * Copyright(c) 2006-2007, Ext JS, LLC.
3137  *
3138  * Originally Released Under LGPL - original licence link has changed is not relivant.
3139  *
3140  * Fork - LGPL
3141  * <script type="text/javascript">
3142  */
3143  
3144 /**
3145  * @class Roo.bootstrap.MenuMgr
3146  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3147  * @singleton
3148  */
3149 Roo.bootstrap.MenuMgr = function(){
3150    var menus, active, groups = {}, attached = false, lastShow = new Date();
3151
3152    // private - called when first menu is created
3153    function init(){
3154        menus = {};
3155        active = new Roo.util.MixedCollection();
3156        Roo.get(document).addKeyListener(27, function(){
3157            if(active.length > 0){
3158                hideAll();
3159            }
3160        });
3161    }
3162
3163    // private
3164    function hideAll(){
3165        if(active && active.length > 0){
3166            var c = active.clone();
3167            c.each(function(m){
3168                m.hide();
3169            });
3170        }
3171    }
3172
3173    // private
3174    function onHide(m){
3175        active.remove(m);
3176        if(active.length < 1){
3177            Roo.get(document).un("mouseup", onMouseDown);
3178             
3179            attached = false;
3180        }
3181    }
3182
3183    // private
3184    function onShow(m){
3185        var last = active.last();
3186        lastShow = new Date();
3187        active.add(m);
3188        if(!attached){
3189           Roo.get(document).on("mouseup", onMouseDown);
3190            
3191            attached = true;
3192        }
3193        if(m.parentMenu){
3194           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3195           m.parentMenu.activeChild = m;
3196        }else if(last && last.isVisible()){
3197           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3198        }
3199    }
3200
3201    // private
3202    function onBeforeHide(m){
3203        if(m.activeChild){
3204            m.activeChild.hide();
3205        }
3206        if(m.autoHideTimer){
3207            clearTimeout(m.autoHideTimer);
3208            delete m.autoHideTimer;
3209        }
3210    }
3211
3212    // private
3213    function onBeforeShow(m){
3214        var pm = m.parentMenu;
3215        if(!pm && !m.allowOtherMenus){
3216            hideAll();
3217        }else if(pm && pm.activeChild && active != m){
3218            pm.activeChild.hide();
3219        }
3220    }
3221
3222    // private this should really trigger on mouseup..
3223    function onMouseDown(e){
3224         Roo.log("on Mouse Up");
3225         
3226         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3227             Roo.log("MenuManager hideAll");
3228             hideAll();
3229             e.stopEvent();
3230         }
3231         
3232         
3233    }
3234
3235    // private
3236    function onBeforeCheck(mi, state){
3237        if(state){
3238            var g = groups[mi.group];
3239            for(var i = 0, l = g.length; i < l; i++){
3240                if(g[i] != mi){
3241                    g[i].setChecked(false);
3242                }
3243            }
3244        }
3245    }
3246
3247    return {
3248
3249        /**
3250         * Hides all menus that are currently visible
3251         */
3252        hideAll : function(){
3253             hideAll();  
3254        },
3255
3256        // private
3257        register : function(menu){
3258            if(!menus){
3259                init();
3260            }
3261            menus[menu.id] = menu;
3262            menu.on("beforehide", onBeforeHide);
3263            menu.on("hide", onHide);
3264            menu.on("beforeshow", onBeforeShow);
3265            menu.on("show", onShow);
3266            var g = menu.group;
3267            if(g && menu.events["checkchange"]){
3268                if(!groups[g]){
3269                    groups[g] = [];
3270                }
3271                groups[g].push(menu);
3272                menu.on("checkchange", onCheck);
3273            }
3274        },
3275
3276         /**
3277          * Returns a {@link Roo.menu.Menu} object
3278          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3279          * be used to generate and return a new Menu instance.
3280          */
3281        get : function(menu){
3282            if(typeof menu == "string"){ // menu id
3283                return menus[menu];
3284            }else if(menu.events){  // menu instance
3285                return menu;
3286            }
3287            /*else if(typeof menu.length == 'number'){ // array of menu items?
3288                return new Roo.bootstrap.Menu({items:menu});
3289            }else{ // otherwise, must be a config
3290                return new Roo.bootstrap.Menu(menu);
3291            }
3292            */
3293            return false;
3294        },
3295
3296        // private
3297        unregister : function(menu){
3298            delete menus[menu.id];
3299            menu.un("beforehide", onBeforeHide);
3300            menu.un("hide", onHide);
3301            menu.un("beforeshow", onBeforeShow);
3302            menu.un("show", onShow);
3303            var g = menu.group;
3304            if(g && menu.events["checkchange"]){
3305                groups[g].remove(menu);
3306                menu.un("checkchange", onCheck);
3307            }
3308        },
3309
3310        // private
3311        registerCheckable : function(menuItem){
3312            var g = menuItem.group;
3313            if(g){
3314                if(!groups[g]){
3315                    groups[g] = [];
3316                }
3317                groups[g].push(menuItem);
3318                menuItem.on("beforecheckchange", onBeforeCheck);
3319            }
3320        },
3321
3322        // private
3323        unregisterCheckable : function(menuItem){
3324            var g = menuItem.group;
3325            if(g){
3326                groups[g].remove(menuItem);
3327                menuItem.un("beforecheckchange", onBeforeCheck);
3328            }
3329        }
3330    };
3331 }();/*
3332  * - LGPL
3333  *
3334  * menu
3335  * 
3336  */
3337
3338 /**
3339  * @class Roo.bootstrap.Menu
3340  * @extends Roo.bootstrap.Component
3341  * Bootstrap Menu class - container for MenuItems
3342  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3343  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3344  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3345  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3346  * 
3347  * @constructor
3348  * Create a new Menu
3349  * @param {Object} config The config object
3350  */
3351
3352
3353 Roo.bootstrap.Menu = function(config){
3354     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3355     if (this.registerMenu && this.type != 'treeview')  {
3356         Roo.bootstrap.MenuMgr.register(this);
3357     }
3358     
3359     
3360     this.addEvents({
3361         /**
3362          * @event beforeshow
3363          * Fires before this menu is displayed (return false to block)
3364          * @param {Roo.menu.Menu} this
3365          */
3366         beforeshow : true,
3367         /**
3368          * @event beforehide
3369          * Fires before this menu is hidden (return false to block)
3370          * @param {Roo.menu.Menu} this
3371          */
3372         beforehide : true,
3373         /**
3374          * @event show
3375          * Fires after this menu is displayed
3376          * @param {Roo.menu.Menu} this
3377          */
3378         show : true,
3379         /**
3380          * @event hide
3381          * Fires after this menu is hidden
3382          * @param {Roo.menu.Menu} this
3383          */
3384         hide : true,
3385         /**
3386          * @event click
3387          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3388          * @param {Roo.menu.Menu} this
3389          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3390          * @param {Roo.EventObject} e
3391          */
3392         click : true,
3393         /**
3394          * @event mouseover
3395          * Fires when the mouse is hovering over this menu
3396          * @param {Roo.menu.Menu} this
3397          * @param {Roo.EventObject} e
3398          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3399          */
3400         mouseover : true,
3401         /**
3402          * @event mouseout
3403          * Fires when the mouse exits this menu
3404          * @param {Roo.menu.Menu} this
3405          * @param {Roo.EventObject} e
3406          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3407          */
3408         mouseout : true,
3409         /**
3410          * @event itemclick
3411          * Fires when a menu item contained in this menu is clicked
3412          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3413          * @param {Roo.EventObject} e
3414          */
3415         itemclick: true
3416     });
3417     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3418 };
3419
3420 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3421     
3422    /// html : false,
3423     //align : '',
3424     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3425     type: false,
3426     /**
3427      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3428      */
3429     registerMenu : true,
3430     
3431     menuItems :false, // stores the menu items..
3432     
3433     hidden:true,
3434         
3435     parentMenu : false,
3436     
3437     stopEvent : true,
3438     
3439     isLink : false,
3440     
3441     getChildContainer : function() {
3442         return this.el;  
3443     },
3444     
3445     getAutoCreate : function(){
3446          
3447         //if (['right'].indexOf(this.align)!==-1) {
3448         //    cfg.cn[1].cls += ' pull-right'
3449         //}
3450         
3451         
3452         var cfg = {
3453             tag : 'ul',
3454             cls : 'dropdown-menu' ,
3455             style : 'z-index:1000'
3456             
3457         };
3458         
3459         if (this.type === 'submenu') {
3460             cfg.cls = 'submenu active';
3461         }
3462         if (this.type === 'treeview') {
3463             cfg.cls = 'treeview-menu';
3464         }
3465         
3466         return cfg;
3467     },
3468     initEvents : function() {
3469         
3470        // Roo.log("ADD event");
3471        // Roo.log(this.triggerEl.dom);
3472         
3473         this.triggerEl.on('click', this.onTriggerClick, this);
3474         
3475         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3476         
3477         
3478         if (this.triggerEl.hasClass('nav-item')) {
3479             // dropdown toggle on the 'a' in BS4?
3480             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3481         } else {
3482             this.triggerEl.addClass('dropdown-toggle');
3483         }
3484         if (Roo.isTouch) {
3485             this.el.on('touchstart'  , this.onTouch, this);
3486         }
3487         this.el.on('click' , this.onClick, this);
3488
3489         this.el.on("mouseover", this.onMouseOver, this);
3490         this.el.on("mouseout", this.onMouseOut, this);
3491         
3492     },
3493     
3494     findTargetItem : function(e)
3495     {
3496         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3497         if(!t){
3498             return false;
3499         }
3500         //Roo.log(t);         Roo.log(t.id);
3501         if(t && t.id){
3502             //Roo.log(this.menuitems);
3503             return this.menuitems.get(t.id);
3504             
3505             //return this.items.get(t.menuItemId);
3506         }
3507         
3508         return false;
3509     },
3510     
3511     onTouch : function(e) 
3512     {
3513         Roo.log("menu.onTouch");
3514         //e.stopEvent(); this make the user popdown broken
3515         this.onClick(e);
3516     },
3517     
3518     onClick : function(e)
3519     {
3520         Roo.log("menu.onClick");
3521         
3522         var t = this.findTargetItem(e);
3523         if(!t || t.isContainer){
3524             return;
3525         }
3526         Roo.log(e);
3527         /*
3528         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3529             if(t == this.activeItem && t.shouldDeactivate(e)){
3530                 this.activeItem.deactivate();
3531                 delete this.activeItem;
3532                 return;
3533             }
3534             if(t.canActivate){
3535                 this.setActiveItem(t, true);
3536             }
3537             return;
3538             
3539             
3540         }
3541         */
3542        
3543         Roo.log('pass click event');
3544         
3545         t.onClick(e);
3546         
3547         this.fireEvent("click", this, t, e);
3548         
3549         var _this = this;
3550         
3551         if(!t.href.length || t.href == '#'){
3552             (function() { _this.hide(); }).defer(100);
3553         }
3554         
3555     },
3556     
3557     onMouseOver : function(e){
3558         var t  = this.findTargetItem(e);
3559         //Roo.log(t);
3560         //if(t){
3561         //    if(t.canActivate && !t.disabled){
3562         //        this.setActiveItem(t, true);
3563         //    }
3564         //}
3565         
3566         this.fireEvent("mouseover", this, e, t);
3567     },
3568     isVisible : function(){
3569         return !this.hidden;
3570     },
3571     onMouseOut : function(e){
3572         var t  = this.findTargetItem(e);
3573         
3574         //if(t ){
3575         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3576         //        this.activeItem.deactivate();
3577         //        delete this.activeItem;
3578         //    }
3579         //}
3580         this.fireEvent("mouseout", this, e, t);
3581     },
3582     
3583     
3584     /**
3585      * Displays this menu relative to another element
3586      * @param {String/HTMLElement/Roo.Element} element The element to align to
3587      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3588      * the element (defaults to this.defaultAlign)
3589      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3590      */
3591     show : function(el, pos, parentMenu)
3592     {
3593         if (false === this.fireEvent("beforeshow", this)) {
3594             Roo.log("show canceled");
3595             return;
3596         }
3597         this.parentMenu = parentMenu;
3598         if(!this.el){
3599             this.render();
3600         }
3601         
3602         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3603     },
3604      /**
3605      * Displays this menu at a specific xy position
3606      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3607      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3608      */
3609     showAt : function(xy, parentMenu, /* private: */_e){
3610         this.parentMenu = parentMenu;
3611         if(!this.el){
3612             this.render();
3613         }
3614         if(_e !== false){
3615             this.fireEvent("beforeshow", this);
3616             //xy = this.el.adjustForConstraints(xy);
3617         }
3618         
3619         //this.el.show();
3620         this.hideMenuItems();
3621         this.hidden = false;
3622         this.triggerEl.addClass('open');
3623         this.el.addClass('show');
3624         
3625         // reassign x when hitting right
3626         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3627             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3628         }
3629         
3630         // reassign y when hitting bottom
3631         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3632             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3633         }
3634         
3635         // but the list may align on trigger left or trigger top... should it be a properity?
3636         
3637         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3638             this.el.setXY(xy);
3639         }
3640         
3641         this.focus();
3642         this.fireEvent("show", this);
3643     },
3644     
3645     focus : function(){
3646         return;
3647         if(!this.hidden){
3648             this.doFocus.defer(50, this);
3649         }
3650     },
3651
3652     doFocus : function(){
3653         if(!this.hidden){
3654             this.focusEl.focus();
3655         }
3656     },
3657
3658     /**
3659      * Hides this menu and optionally all parent menus
3660      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3661      */
3662     hide : function(deep)
3663     {
3664         if (false === this.fireEvent("beforehide", this)) {
3665             Roo.log("hide canceled");
3666             return;
3667         }
3668         this.hideMenuItems();
3669         if(this.el && this.isVisible()){
3670            
3671             if(this.activeItem){
3672                 this.activeItem.deactivate();
3673                 this.activeItem = null;
3674             }
3675             this.triggerEl.removeClass('open');;
3676             this.el.removeClass('show');
3677             this.hidden = true;
3678             this.fireEvent("hide", this);
3679         }
3680         if(deep === true && this.parentMenu){
3681             this.parentMenu.hide(true);
3682         }
3683     },
3684     
3685     onTriggerClick : function(e)
3686     {
3687         Roo.log('trigger click');
3688         
3689         var target = e.getTarget();
3690         
3691         Roo.log(target.nodeName.toLowerCase());
3692         
3693         if(target.nodeName.toLowerCase() === 'i'){
3694             e.preventDefault();
3695         }
3696         
3697     },
3698     
3699     onTriggerPress  : function(e)
3700     {
3701         Roo.log('trigger press');
3702         //Roo.log(e.getTarget());
3703        // Roo.log(this.triggerEl.dom);
3704        
3705         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3706         var pel = Roo.get(e.getTarget());
3707         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3708             Roo.log('is treeview or dropdown?');
3709             return;
3710         }
3711         
3712         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3713             return;
3714         }
3715         
3716         if (this.isVisible()) {
3717             Roo.log('hide');
3718             this.hide();
3719         } else {
3720             Roo.log('show');
3721             this.show(this.triggerEl, '?', false);
3722         }
3723         
3724         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3725             e.stopEvent();
3726         }
3727         
3728     },
3729        
3730     
3731     hideMenuItems : function()
3732     {
3733         Roo.log("hide Menu Items");
3734         if (!this.el) { 
3735             return;
3736         }
3737         
3738         this.el.select('.open',true).each(function(aa) {
3739             
3740             aa.removeClass('open');
3741          
3742         });
3743     },
3744     addxtypeChild : function (tree, cntr) {
3745         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3746           
3747         this.menuitems.add(comp);
3748         return comp;
3749
3750     },
3751     getEl : function()
3752     {
3753         Roo.log(this.el);
3754         return this.el;
3755     },
3756     
3757     clear : function()
3758     {
3759         this.getEl().dom.innerHTML = '';
3760         this.menuitems.clear();
3761     }
3762 });
3763
3764  
3765  /*
3766  * - LGPL
3767  *
3768  * menu item
3769  * 
3770  */
3771
3772
3773 /**
3774  * @class Roo.bootstrap.MenuItem
3775  * @extends Roo.bootstrap.Component
3776  * Bootstrap MenuItem class
3777  * @cfg {String} html the menu label
3778  * @cfg {String} href the link
3779  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3780  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3781  * @cfg {Boolean} active  used on sidebars to highlight active itesm
3782  * @cfg {String} fa favicon to show on left of menu item.
3783  * @cfg {Roo.bootsrap.Menu} menu the child menu.
3784  * 
3785  * 
3786  * @constructor
3787  * Create a new MenuItem
3788  * @param {Object} config The config object
3789  */
3790
3791
3792 Roo.bootstrap.MenuItem = function(config){
3793     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3794     this.addEvents({
3795         // raw events
3796         /**
3797          * @event click
3798          * The raw click event for the entire grid.
3799          * @param {Roo.bootstrap.MenuItem} this
3800          * @param {Roo.EventObject} e
3801          */
3802         "click" : true
3803     });
3804 };
3805
3806 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
3807     
3808     href : false,
3809     html : false,
3810     preventDefault: false,
3811     isContainer : false,
3812     active : false,
3813     fa: false,
3814     
3815     getAutoCreate : function(){
3816         
3817         if(this.isContainer){
3818             return {
3819                 tag: 'li',
3820                 cls: 'dropdown-menu-item '
3821             };
3822         }
3823         var ctag = {
3824             tag: 'span',
3825             html: 'Link'
3826         };
3827         
3828         var anc = {
3829             tag : 'a',
3830             cls : 'dropdown-item',
3831             href : '#',
3832             cn : [  ]
3833         };
3834         
3835         if (this.fa !== false) {
3836             anc.cn.push({
3837                 tag : 'i',
3838                 cls : 'fa fa-' + this.fa
3839             });
3840         }
3841         
3842         anc.cn.push(ctag);
3843         
3844         
3845         var cfg= {
3846             tag: 'li',
3847             cls: 'dropdown-menu-item',
3848             cn: [ anc ]
3849         };
3850         if (this.parent().type == 'treeview') {
3851             cfg.cls = 'treeview-menu';
3852         }
3853         if (this.active) {
3854             cfg.cls += ' active';
3855         }
3856         
3857         
3858         
3859         anc.href = this.href || cfg.cn[0].href ;
3860         ctag.html = this.html || cfg.cn[0].html ;
3861         return cfg;
3862     },
3863     
3864     initEvents: function()
3865     {
3866         if (this.parent().type == 'treeview') {
3867             this.el.select('a').on('click', this.onClick, this);
3868         }
3869         
3870         if (this.menu) {
3871             this.menu.parentType = this.xtype;
3872             this.menu.triggerEl = this.el;
3873             this.menu = this.addxtype(Roo.apply({}, this.menu));
3874         }
3875         
3876     },
3877     onClick : function(e)
3878     {
3879         Roo.log('item on click ');
3880         
3881         if(this.preventDefault){
3882             e.preventDefault();
3883         }
3884         //this.parent().hideMenuItems();
3885         
3886         this.fireEvent('click', this, e);
3887     },
3888     getEl : function()
3889     {
3890         return this.el;
3891     } 
3892 });
3893
3894  
3895
3896  /*
3897  * - LGPL
3898  *
3899  * menu separator
3900  * 
3901  */
3902
3903
3904 /**
3905  * @class Roo.bootstrap.MenuSeparator
3906  * @extends Roo.bootstrap.Component
3907  * Bootstrap MenuSeparator class
3908  * 
3909  * @constructor
3910  * Create a new MenuItem
3911  * @param {Object} config The config object
3912  */
3913
3914
3915 Roo.bootstrap.MenuSeparator = function(config){
3916     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3917 };
3918
3919 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
3920     
3921     getAutoCreate : function(){
3922         var cfg = {
3923             cls: 'divider',
3924             tag : 'li'
3925         };
3926         
3927         return cfg;
3928     }
3929    
3930 });
3931
3932  
3933
3934  
3935 /*
3936 * Licence: LGPL
3937 */
3938
3939 /**
3940  * @class Roo.bootstrap.Modal
3941  * @extends Roo.bootstrap.Component
3942  * Bootstrap Modal class
3943  * @cfg {String} title Title of dialog
3944  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3945  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
3946  * @cfg {Boolean} specificTitle default false
3947  * @cfg {Array} buttons Array of buttons or standard button set..
3948  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3949  * @cfg {Boolean} animate default true
3950  * @cfg {Boolean} allow_close default true
3951  * @cfg {Boolean} fitwindow default false
3952  * @cfg {Number} width fixed width - usefull for chrome extension only really.
3953  * @cfg {Number} height fixed height - usefull for chrome extension only really.
3954  * @cfg {String} size (sm|lg|xl) default empty
3955  * @cfg {Number} max_width set the max width of modal
3956  * @cfg {Boolean} editableTitle can the title be edited
3957
3958  *
3959  *
3960  * @constructor
3961  * Create a new Modal Dialog
3962  * @param {Object} config The config object
3963  */
3964
3965 Roo.bootstrap.Modal = function(config){
3966     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3967     this.addEvents({
3968         // raw events
3969         /**
3970          * @event btnclick
3971          * The raw btnclick event for the button
3972          * @param {Roo.EventObject} e
3973          */
3974         "btnclick" : true,
3975         /**
3976          * @event resize
3977          * Fire when dialog resize
3978          * @param {Roo.bootstrap.Modal} this
3979          * @param {Roo.EventObject} e
3980          */
3981         "resize" : true,
3982         /**
3983          * @event titlechanged
3984          * Fire when the editable title has been changed
3985          * @param {Roo.bootstrap.Modal} this
3986          * @param {Roo.EventObject} value
3987          */
3988         "titlechanged" : true 
3989         
3990     });
3991     this.buttons = this.buttons || [];
3992
3993     if (this.tmpl) {
3994         this.tmpl = Roo.factory(this.tmpl);
3995     }
3996
3997 };
3998
3999 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4000
4001     title : 'test dialog',
4002
4003     buttons : false,
4004
4005     // set on load...
4006
4007     html: false,
4008
4009     tmp: false,
4010
4011     specificTitle: false,
4012
4013     buttonPosition: 'right',
4014
4015     allow_close : true,
4016
4017     animate : true,
4018
4019     fitwindow: false,
4020     
4021      // private
4022     dialogEl: false,
4023     bodyEl:  false,
4024     footerEl:  false,
4025     titleEl:  false,
4026     closeEl:  false,
4027
4028     size: '',
4029     
4030     max_width: 0,
4031     
4032     max_height: 0,
4033     
4034     fit_content: false,
4035     editableTitle  : false,
4036
4037     onRender : function(ct, position)
4038     {
4039         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4040
4041         if(!this.el){
4042             var cfg = Roo.apply({},  this.getAutoCreate());
4043             cfg.id = Roo.id();
4044             //if(!cfg.name){
4045             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4046             //}
4047             //if (!cfg.name.length) {
4048             //    delete cfg.name;
4049            // }
4050             if (this.cls) {
4051                 cfg.cls += ' ' + this.cls;
4052             }
4053             if (this.style) {
4054                 cfg.style = this.style;
4055             }
4056             this.el = Roo.get(document.body).createChild(cfg, position);
4057         }
4058         //var type = this.el.dom.type;
4059
4060
4061         if(this.tabIndex !== undefined){
4062             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4063         }
4064
4065         this.dialogEl = this.el.select('.modal-dialog',true).first();
4066         this.bodyEl = this.el.select('.modal-body',true).first();
4067         this.closeEl = this.el.select('.modal-header .close', true).first();
4068         this.headerEl = this.el.select('.modal-header',true).first();
4069         this.titleEl = this.el.select('.modal-title',true).first();
4070         this.footerEl = this.el.select('.modal-footer',true).first();
4071
4072         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4073         
4074         //this.el.addClass("x-dlg-modal");
4075
4076         if (this.buttons.length) {
4077             Roo.each(this.buttons, function(bb) {
4078                 var b = Roo.apply({}, bb);
4079                 b.xns = b.xns || Roo.bootstrap;
4080                 b.xtype = b.xtype || 'Button';
4081                 if (typeof(b.listeners) == 'undefined') {
4082                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4083                 }
4084
4085                 var btn = Roo.factory(b);
4086
4087                 btn.render(this.getButtonContainer());
4088
4089             },this);
4090         }
4091         // render the children.
4092         var nitems = [];
4093
4094         if(typeof(this.items) != 'undefined'){
4095             var items = this.items;
4096             delete this.items;
4097
4098             for(var i =0;i < items.length;i++) {
4099                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4100             }
4101         }
4102
4103         this.items = nitems;
4104
4105         // where are these used - they used to be body/close/footer
4106
4107
4108         this.initEvents();
4109         //this.el.addClass([this.fieldClass, this.cls]);
4110
4111     },
4112
4113     getAutoCreate : function()
4114     {
4115         // we will default to modal-body-overflow - might need to remove or make optional later.
4116         var bdy = {
4117                 cls : 'modal-body enable-modal-body-overflow ', 
4118                 html : this.html || ''
4119         };
4120
4121         var title = {
4122             tag: 'h4',
4123             cls : 'modal-title',
4124             html : this.title
4125         };
4126
4127         if(this.specificTitle){ // WTF is this?
4128             title = this.title;
4129         }
4130
4131         var header = [];
4132         if (this.allow_close && Roo.bootstrap.version == 3) {
4133             header.push({
4134                 tag: 'button',
4135                 cls : 'close',
4136                 html : '&times'
4137             });
4138         }
4139
4140         header.push(title);
4141
4142         if (this.editableTitle) {
4143             header.push({
4144                 cls: 'form-control roo-editable-title d-none',
4145                 tag: 'input',
4146                 type: 'text'
4147             });
4148         }
4149         
4150         if (this.allow_close && Roo.bootstrap.version == 4) {
4151             header.push({
4152                 tag: 'button',
4153                 cls : 'close',
4154                 html : '&times'
4155             });
4156         }
4157         
4158         var size = '';
4159
4160         if(this.size.length){
4161             size = 'modal-' + this.size;
4162         }
4163         
4164         var footer = Roo.bootstrap.version == 3 ?
4165             {
4166                 cls : 'modal-footer',
4167                 cn : [
4168                     {
4169                         tag: 'div',
4170                         cls: 'btn-' + this.buttonPosition
4171                     }
4172                 ]
4173
4174             } :
4175             {  // BS4 uses mr-auto on left buttons....
4176                 cls : 'modal-footer'
4177             };
4178
4179             
4180
4181         
4182         
4183         var modal = {
4184             cls: "modal",
4185              cn : [
4186                 {
4187                     cls: "modal-dialog " + size,
4188                     cn : [
4189                         {
4190                             cls : "modal-content",
4191                             cn : [
4192                                 {
4193                                     cls : 'modal-header',
4194                                     cn : header
4195                                 },
4196                                 bdy,
4197                                 footer
4198                             ]
4199
4200                         }
4201                     ]
4202
4203                 }
4204             ]
4205         };
4206
4207         if(this.animate){
4208             modal.cls += ' fade';
4209         }
4210
4211         return modal;
4212
4213     },
4214     getChildContainer : function() {
4215
4216          return this.bodyEl;
4217
4218     },
4219     getButtonContainer : function() {
4220         
4221          return Roo.bootstrap.version == 4 ?
4222             this.el.select('.modal-footer',true).first()
4223             : this.el.select('.modal-footer div',true).first();
4224
4225     },
4226     initEvents : function()
4227     {
4228         if (this.allow_close) {
4229             this.closeEl.on('click', this.hide, this);
4230         }
4231         Roo.EventManager.onWindowResize(this.resize, this, true);
4232         if (this.editableTitle) {
4233             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4234             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4235             this.headerEditEl.on('keyup', function(e) {
4236                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4237                         this.toggleHeaderInput(false)
4238                     }
4239                 }, this);
4240             this.headerEditEl.on('blur', function(e) {
4241                 this.toggleHeaderInput(false)
4242             },this);
4243         }
4244
4245     },
4246   
4247
4248     resize : function()
4249     {
4250         this.maskEl.setSize(
4251             Roo.lib.Dom.getViewWidth(true),
4252             Roo.lib.Dom.getViewHeight(true)
4253         );
4254         
4255         if (this.fitwindow) {
4256             
4257            
4258             this.setSize(
4259                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4260                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4261             );
4262             return;
4263         }
4264         
4265         if(this.max_width !== 0) {
4266             
4267             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4268             
4269             if(this.height) {
4270                 this.setSize(w, this.height);
4271                 return;
4272             }
4273             
4274             if(this.max_height) {
4275                 this.setSize(w,Math.min(
4276                     this.max_height,
4277                     Roo.lib.Dom.getViewportHeight(true) - 60
4278                 ));
4279                 
4280                 return;
4281             }
4282             
4283             if(!this.fit_content) {
4284                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4285                 return;
4286             }
4287             
4288             this.setSize(w, Math.min(
4289                 60 +
4290                 this.headerEl.getHeight() + 
4291                 this.footerEl.getHeight() + 
4292                 this.getChildHeight(this.bodyEl.dom.childNodes),
4293                 Roo.lib.Dom.getViewportHeight(true) - 60)
4294             );
4295         }
4296         
4297     },
4298
4299     setSize : function(w,h)
4300     {
4301         if (!w && !h) {
4302             return;
4303         }
4304         
4305         this.resizeTo(w,h);
4306     },
4307
4308     show : function() {
4309
4310         if (!this.rendered) {
4311             this.render();
4312         }
4313         this.toggleHeaderInput(false);
4314         //this.el.setStyle('display', 'block');
4315         this.el.removeClass('hideing');
4316         this.el.dom.style.display='block';
4317         
4318         Roo.get(document.body).addClass('modal-open');
4319  
4320         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4321             
4322             (function(){
4323                 this.el.addClass('show');
4324                 this.el.addClass('in');
4325             }).defer(50, this);
4326         }else{
4327             this.el.addClass('show');
4328             this.el.addClass('in');
4329         }
4330
4331         // not sure how we can show data in here..
4332         //if (this.tmpl) {
4333         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4334         //}
4335
4336         Roo.get(document.body).addClass("x-body-masked");
4337         
4338         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4339         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4340         this.maskEl.dom.style.display = 'block';
4341         this.maskEl.addClass('show');
4342         
4343         
4344         this.resize();
4345         
4346         this.fireEvent('show', this);
4347
4348         // set zindex here - otherwise it appears to be ignored...
4349         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4350
4351         (function () {
4352             this.items.forEach( function(e) {
4353                 e.layout ? e.layout() : false;
4354
4355             });
4356         }).defer(100,this);
4357
4358     },
4359     hide : function()
4360     {
4361         if(this.fireEvent("beforehide", this) !== false){
4362             
4363             this.maskEl.removeClass('show');
4364             
4365             this.maskEl.dom.style.display = '';
4366             Roo.get(document.body).removeClass("x-body-masked");
4367             this.el.removeClass('in');
4368             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4369
4370             if(this.animate){ // why
4371                 this.el.addClass('hideing');
4372                 this.el.removeClass('show');
4373                 (function(){
4374                     if (!this.el.hasClass('hideing')) {
4375                         return; // it's been shown again...
4376                     }
4377                     
4378                     this.el.dom.style.display='';
4379
4380                     Roo.get(document.body).removeClass('modal-open');
4381                     this.el.removeClass('hideing');
4382                 }).defer(150,this);
4383                 
4384             }else{
4385                 this.el.removeClass('show');
4386                 this.el.dom.style.display='';
4387                 Roo.get(document.body).removeClass('modal-open');
4388
4389             }
4390             this.fireEvent('hide', this);
4391         }
4392     },
4393     isVisible : function()
4394     {
4395         
4396         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4397         
4398     },
4399
4400     addButton : function(str, cb)
4401     {
4402
4403
4404         var b = Roo.apply({}, { html : str } );
4405         b.xns = b.xns || Roo.bootstrap;
4406         b.xtype = b.xtype || 'Button';
4407         if (typeof(b.listeners) == 'undefined') {
4408             b.listeners = { click : cb.createDelegate(this)  };
4409         }
4410
4411         var btn = Roo.factory(b);
4412
4413         btn.render(this.getButtonContainer());
4414
4415         return btn;
4416
4417     },
4418
4419     setDefaultButton : function(btn)
4420     {
4421         //this.el.select('.modal-footer').()
4422     },
4423
4424     resizeTo: function(w,h)
4425     {
4426         this.dialogEl.setWidth(w);
4427         
4428         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4429
4430         this.bodyEl.setHeight(h - diff);
4431         
4432         this.fireEvent('resize', this);
4433     },
4434     
4435     setContentSize  : function(w, h)
4436     {
4437
4438     },
4439     onButtonClick: function(btn,e)
4440     {
4441         //Roo.log([a,b,c]);
4442         this.fireEvent('btnclick', btn.name, e);
4443     },
4444      /**
4445      * Set the title of the Dialog
4446      * @param {String} str new Title
4447      */
4448     setTitle: function(str) {
4449         this.titleEl.dom.innerHTML = str;
4450         this.title = str;
4451     },
4452     /**
4453      * Set the body of the Dialog
4454      * @param {String} str new Title
4455      */
4456     setBody: function(str) {
4457         this.bodyEl.dom.innerHTML = str;
4458     },
4459     /**
4460      * Set the body of the Dialog using the template
4461      * @param {Obj} data - apply this data to the template and replace the body contents.
4462      */
4463     applyBody: function(obj)
4464     {
4465         if (!this.tmpl) {
4466             Roo.log("Error - using apply Body without a template");
4467             //code
4468         }
4469         this.tmpl.overwrite(this.bodyEl, obj);
4470     },
4471     
4472     getChildHeight : function(child_nodes)
4473     {
4474         if(
4475             !child_nodes ||
4476             child_nodes.length == 0
4477         ) {
4478             return 0;
4479         }
4480         
4481         var child_height = 0;
4482         
4483         for(var i = 0; i < child_nodes.length; i++) {
4484             
4485             /*
4486             * for modal with tabs...
4487             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4488                 
4489                 var layout_childs = child_nodes[i].childNodes;
4490                 
4491                 for(var j = 0; j < layout_childs.length; j++) {
4492                     
4493                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4494                         
4495                         var layout_body_childs = layout_childs[j].childNodes;
4496                         
4497                         for(var k = 0; k < layout_body_childs.length; k++) {
4498                             
4499                             if(layout_body_childs[k].classList.contains('navbar')) {
4500                                 child_height += layout_body_childs[k].offsetHeight;
4501                                 continue;
4502                             }
4503                             
4504                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4505                                 
4506                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4507                                 
4508                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4509                                     
4510                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4511                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4512                                         continue;
4513                                     }
4514                                     
4515                                 }
4516                                 
4517                             }
4518                             
4519                         }
4520                     }
4521                 }
4522                 continue;
4523             }
4524             */
4525             
4526             child_height += child_nodes[i].offsetHeight;
4527             // Roo.log(child_nodes[i].offsetHeight);
4528         }
4529         
4530         return child_height;
4531     },
4532     toggleHeaderInput : function(is_edit)
4533     {
4534         if (!this.editableTitle) {
4535             return; // not editable.
4536         }
4537         if (is_edit && this.is_header_editing) {
4538             return; // already editing..
4539         }
4540         if (is_edit) {
4541     
4542             this.headerEditEl.dom.value = this.title;
4543             this.headerEditEl.removeClass('d-none');
4544             this.headerEditEl.dom.focus();
4545             this.titleEl.addClass('d-none');
4546             
4547             this.is_header_editing = true;
4548             return
4549         }
4550         // flip back to not editing.
4551         this.title = this.headerEditEl.dom.value;
4552         this.headerEditEl.addClass('d-none');
4553         this.titleEl.removeClass('d-none');
4554         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4555         this.is_header_editing = false;
4556         this.fireEvent('titlechanged', this, this.title);
4557     
4558             
4559         
4560     }
4561
4562 });
4563
4564
4565 Roo.apply(Roo.bootstrap.Modal,  {
4566     /**
4567          * Button config that displays a single OK button
4568          * @type Object
4569          */
4570         OK :  [{
4571             name : 'ok',
4572             weight : 'primary',
4573             html : 'OK'
4574         }],
4575         /**
4576          * Button config that displays Yes and No buttons
4577          * @type Object
4578          */
4579         YESNO : [
4580             {
4581                 name  : 'no',
4582                 html : 'No'
4583             },
4584             {
4585                 name  :'yes',
4586                 weight : 'primary',
4587                 html : 'Yes'
4588             }
4589         ],
4590
4591         /**
4592          * Button config that displays OK and Cancel buttons
4593          * @type Object
4594          */
4595         OKCANCEL : [
4596             {
4597                name : 'cancel',
4598                 html : 'Cancel'
4599             },
4600             {
4601                 name : 'ok',
4602                 weight : 'primary',
4603                 html : 'OK'
4604             }
4605         ],
4606         /**
4607          * Button config that displays Yes, No and Cancel buttons
4608          * @type Object
4609          */
4610         YESNOCANCEL : [
4611             {
4612                 name : 'yes',
4613                 weight : 'primary',
4614                 html : 'Yes'
4615             },
4616             {
4617                 name : 'no',
4618                 html : 'No'
4619             },
4620             {
4621                 name : 'cancel',
4622                 html : 'Cancel'
4623             }
4624         ],
4625         
4626         zIndex : 10001
4627 });
4628
4629 /*
4630  * - LGPL
4631  *
4632  * messagebox - can be used as a replace
4633  * 
4634  */
4635 /**
4636  * @class Roo.MessageBox
4637  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4638  * Example usage:
4639  *<pre><code>
4640 // Basic alert:
4641 Roo.Msg.alert('Status', 'Changes saved successfully.');
4642
4643 // Prompt for user data:
4644 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4645     if (btn == 'ok'){
4646         // process text value...
4647     }
4648 });
4649
4650 // Show a dialog using config options:
4651 Roo.Msg.show({
4652    title:'Save Changes?',
4653    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4654    buttons: Roo.Msg.YESNOCANCEL,
4655    fn: processResult,
4656    animEl: 'elId'
4657 });
4658 </code></pre>
4659  * @singleton
4660  */
4661 Roo.bootstrap.MessageBox = function(){
4662     var dlg, opt, mask, waitTimer;
4663     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4664     var buttons, activeTextEl, bwidth;
4665
4666     
4667     // private
4668     var handleButton = function(button){
4669         dlg.hide();
4670         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4671     };
4672
4673     // private
4674     var handleHide = function(){
4675         if(opt && opt.cls){
4676             dlg.el.removeClass(opt.cls);
4677         }
4678         //if(waitTimer){
4679         //    Roo.TaskMgr.stop(waitTimer);
4680         //    waitTimer = null;
4681         //}
4682     };
4683
4684     // private
4685     var updateButtons = function(b){
4686         var width = 0;
4687         if(!b){
4688             buttons["ok"].hide();
4689             buttons["cancel"].hide();
4690             buttons["yes"].hide();
4691             buttons["no"].hide();
4692             dlg.footerEl.hide();
4693             
4694             return width;
4695         }
4696         dlg.footerEl.show();
4697         for(var k in buttons){
4698             if(typeof buttons[k] != "function"){
4699                 if(b[k]){
4700                     buttons[k].show();
4701                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4702                     width += buttons[k].el.getWidth()+15;
4703                 }else{
4704                     buttons[k].hide();
4705                 }
4706             }
4707         }
4708         return width;
4709     };
4710
4711     // private
4712     var handleEsc = function(d, k, e){
4713         if(opt && opt.closable !== false){
4714             dlg.hide();
4715         }
4716         if(e){
4717             e.stopEvent();
4718         }
4719     };
4720
4721     return {
4722         /**
4723          * Returns a reference to the underlying {@link Roo.BasicDialog} element
4724          * @return {Roo.BasicDialog} The BasicDialog element
4725          */
4726         getDialog : function(){
4727            if(!dlg){
4728                 dlg = new Roo.bootstrap.Modal( {
4729                     //draggable: true,
4730                     //resizable:false,
4731                     //constraintoviewport:false,
4732                     //fixedcenter:true,
4733                     //collapsible : false,
4734                     //shim:true,
4735                     //modal: true,
4736                 //    width: 'auto',
4737                   //  height:100,
4738                     //buttonAlign:"center",
4739                     closeClick : function(){
4740                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4741                             handleButton("no");
4742                         }else{
4743                             handleButton("cancel");
4744                         }
4745                     }
4746                 });
4747                 dlg.render();
4748                 dlg.on("hide", handleHide);
4749                 mask = dlg.mask;
4750                 //dlg.addKeyListener(27, handleEsc);
4751                 buttons = {};
4752                 this.buttons = buttons;
4753                 var bt = this.buttonText;
4754                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4755                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4756                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4757                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4758                 //Roo.log(buttons);
4759                 bodyEl = dlg.bodyEl.createChild({
4760
4761                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4762                         '<textarea class="roo-mb-textarea"></textarea>' +
4763                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
4764                 });
4765                 msgEl = bodyEl.dom.firstChild;
4766                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4767                 textboxEl.enableDisplayMode();
4768                 textboxEl.addKeyListener([10,13], function(){
4769                     if(dlg.isVisible() && opt && opt.buttons){
4770                         if(opt.buttons.ok){
4771                             handleButton("ok");
4772                         }else if(opt.buttons.yes){
4773                             handleButton("yes");
4774                         }
4775                     }
4776                 });
4777                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4778                 textareaEl.enableDisplayMode();
4779                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4780                 progressEl.enableDisplayMode();
4781                 
4782                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4783                 var pf = progressEl.dom.firstChild;
4784                 if (pf) {
4785                     pp = Roo.get(pf.firstChild);
4786                     pp.setHeight(pf.offsetHeight);
4787                 }
4788                 
4789             }
4790             return dlg;
4791         },
4792
4793         /**
4794          * Updates the message box body text
4795          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4796          * the XHTML-compliant non-breaking space character '&amp;#160;')
4797          * @return {Roo.MessageBox} This message box
4798          */
4799         updateText : function(text)
4800         {
4801             if(!dlg.isVisible() && !opt.width){
4802                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4803                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4804             }
4805             msgEl.innerHTML = text || '&#160;';
4806       
4807             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4808             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4809             var w = Math.max(
4810                     Math.min(opt.width || cw , this.maxWidth), 
4811                     Math.max(opt.minWidth || this.minWidth, bwidth)
4812             );
4813             if(opt.prompt){
4814                 activeTextEl.setWidth(w);
4815             }
4816             if(dlg.isVisible()){
4817                 dlg.fixedcenter = false;
4818             }
4819             // to big, make it scroll. = But as usual stupid IE does not support
4820             // !important..
4821             
4822             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4823                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4824                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4825             } else {
4826                 bodyEl.dom.style.height = '';
4827                 bodyEl.dom.style.overflowY = '';
4828             }
4829             if (cw > w) {
4830                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4831             } else {
4832                 bodyEl.dom.style.overflowX = '';
4833             }
4834             
4835             dlg.setContentSize(w, bodyEl.getHeight());
4836             if(dlg.isVisible()){
4837                 dlg.fixedcenter = true;
4838             }
4839             return this;
4840         },
4841
4842         /**
4843          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
4844          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4845          * @param {Number} value Any number between 0 and 1 (e.g., .5)
4846          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4847          * @return {Roo.MessageBox} This message box
4848          */
4849         updateProgress : function(value, text){
4850             if(text){
4851                 this.updateText(text);
4852             }
4853             
4854             if (pp) { // weird bug on my firefox - for some reason this is not defined
4855                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4856                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4857             }
4858             return this;
4859         },        
4860
4861         /**
4862          * Returns true if the message box is currently displayed
4863          * @return {Boolean} True if the message box is visible, else false
4864          */
4865         isVisible : function(){
4866             return dlg && dlg.isVisible();  
4867         },
4868
4869         /**
4870          * Hides the message box if it is displayed
4871          */
4872         hide : function(){
4873             if(this.isVisible()){
4874                 dlg.hide();
4875             }  
4876         },
4877
4878         /**
4879          * Displays a new message box, or reinitializes an existing message box, based on the config options
4880          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4881          * The following config object properties are supported:
4882          * <pre>
4883 Property    Type             Description
4884 ----------  ---------------  ------------------------------------------------------------------------------------
4885 animEl            String/Element   An id or Element from which the message box should animate as it opens and
4886                                    closes (defaults to undefined)
4887 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4888                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
4889 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
4890                                    progress and wait dialogs will ignore this property and always hide the
4891                                    close button as they can only be closed programmatically.
4892 cls               String           A custom CSS class to apply to the message box element
4893 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
4894                                    displayed (defaults to 75)
4895 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
4896                                    function will be btn (the name of the button that was clicked, if applicable,
4897                                    e.g. "ok"), and text (the value of the active text field, if applicable).
4898                                    Progress and wait dialogs will ignore this option since they do not respond to
4899                                    user actions and can only be closed programmatically, so any required function
4900                                    should be called by the same code after it closes the dialog.
4901 icon              String           A CSS class that provides a background image to be used as an icon for
4902                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4903 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
4904 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
4905 modal             Boolean          False to allow user interaction with the page while the message box is
4906                                    displayed (defaults to true)
4907 msg               String           A string that will replace the existing message box body text (defaults
4908                                    to the XHTML-compliant non-breaking space character '&#160;')
4909 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
4910 progress          Boolean          True to display a progress bar (defaults to false)
4911 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
4912 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
4913 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
4914 title             String           The title text
4915 value             String           The string value to set into the active textbox element if displayed
4916 wait              Boolean          True to display a progress bar (defaults to false)
4917 width             Number           The width of the dialog in pixels
4918 </pre>
4919          *
4920          * Example usage:
4921          * <pre><code>
4922 Roo.Msg.show({
4923    title: 'Address',
4924    msg: 'Please enter your address:',
4925    width: 300,
4926    buttons: Roo.MessageBox.OKCANCEL,
4927    multiline: true,
4928    fn: saveAddress,
4929    animEl: 'addAddressBtn'
4930 });
4931 </code></pre>
4932          * @param {Object} config Configuration options
4933          * @return {Roo.MessageBox} This message box
4934          */
4935         show : function(options)
4936         {
4937             
4938             // this causes nightmares if you show one dialog after another
4939             // especially on callbacks..
4940              
4941             if(this.isVisible()){
4942                 
4943                 this.hide();
4944                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4945                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
4946                 Roo.log("New Dialog Message:" +  options.msg )
4947                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4948                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4949                 
4950             }
4951             var d = this.getDialog();
4952             opt = options;
4953             d.setTitle(opt.title || "&#160;");
4954             d.closeEl.setDisplayed(opt.closable !== false);
4955             activeTextEl = textboxEl;
4956             opt.prompt = opt.prompt || (opt.multiline ? true : false);
4957             if(opt.prompt){
4958                 if(opt.multiline){
4959                     textboxEl.hide();
4960                     textareaEl.show();
4961                     textareaEl.setHeight(typeof opt.multiline == "number" ?
4962                         opt.multiline : this.defaultTextHeight);
4963                     activeTextEl = textareaEl;
4964                 }else{
4965                     textboxEl.show();
4966                     textareaEl.hide();
4967                 }
4968             }else{
4969                 textboxEl.hide();
4970                 textareaEl.hide();
4971             }
4972             progressEl.setDisplayed(opt.progress === true);
4973             if (opt.progress) {
4974                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4975             }
4976             this.updateProgress(0);
4977             activeTextEl.dom.value = opt.value || "";
4978             if(opt.prompt){
4979                 dlg.setDefaultButton(activeTextEl);
4980             }else{
4981                 var bs = opt.buttons;
4982                 var db = null;
4983                 if(bs && bs.ok){
4984                     db = buttons["ok"];
4985                 }else if(bs && bs.yes){
4986                     db = buttons["yes"];
4987                 }
4988                 dlg.setDefaultButton(db);
4989             }
4990             bwidth = updateButtons(opt.buttons);
4991             this.updateText(opt.msg);
4992             if(opt.cls){
4993                 d.el.addClass(opt.cls);
4994             }
4995             d.proxyDrag = opt.proxyDrag === true;
4996             d.modal = opt.modal !== false;
4997             d.mask = opt.modal !== false ? mask : false;
4998             if(!d.isVisible()){
4999                 // force it to the end of the z-index stack so it gets a cursor in FF
5000                 document.body.appendChild(dlg.el.dom);
5001                 d.animateTarget = null;
5002                 d.show(options.animEl);
5003             }
5004             return this;
5005         },
5006
5007         /**
5008          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5009          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5010          * and closing the message box when the process is complete.
5011          * @param {String} title The title bar text
5012          * @param {String} msg The message box body text
5013          * @return {Roo.MessageBox} This message box
5014          */
5015         progress : function(title, msg){
5016             this.show({
5017                 title : title,
5018                 msg : msg,
5019                 buttons: false,
5020                 progress:true,
5021                 closable:false,
5022                 minWidth: this.minProgressWidth,
5023                 modal : true
5024             });
5025             return this;
5026         },
5027
5028         /**
5029          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5030          * If a callback function is passed it will be called after the user clicks the button, and the
5031          * id of the button that was clicked will be passed as the only parameter to the callback
5032          * (could also be the top-right close button).
5033          * @param {String} title The title bar text
5034          * @param {String} msg The message box body text
5035          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5036          * @param {Object} scope (optional) The scope of the callback function
5037          * @return {Roo.MessageBox} This message box
5038          */
5039         alert : function(title, msg, fn, scope)
5040         {
5041             this.show({
5042                 title : title,
5043                 msg : msg,
5044                 buttons: this.OK,
5045                 fn: fn,
5046                 closable : false,
5047                 scope : scope,
5048                 modal : true
5049             });
5050             return this;
5051         },
5052
5053         /**
5054          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5055          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5056          * You are responsible for closing the message box when the process is complete.
5057          * @param {String} msg The message box body text
5058          * @param {String} title (optional) The title bar text
5059          * @return {Roo.MessageBox} This message box
5060          */
5061         wait : function(msg, title){
5062             this.show({
5063                 title : title,
5064                 msg : msg,
5065                 buttons: false,
5066                 closable:false,
5067                 progress:true,
5068                 modal:true,
5069                 width:300,
5070                 wait:true
5071             });
5072             waitTimer = Roo.TaskMgr.start({
5073                 run: function(i){
5074                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5075                 },
5076                 interval: 1000
5077             });
5078             return this;
5079         },
5080
5081         /**
5082          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5083          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5084          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5085          * @param {String} title The title bar text
5086          * @param {String} msg The message box body text
5087          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5088          * @param {Object} scope (optional) The scope of the callback function
5089          * @return {Roo.MessageBox} This message box
5090          */
5091         confirm : function(title, msg, fn, scope){
5092             this.show({
5093                 title : title,
5094                 msg : msg,
5095                 buttons: this.YESNO,
5096                 fn: fn,
5097                 scope : scope,
5098                 modal : true
5099             });
5100             return this;
5101         },
5102
5103         /**
5104          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5105          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5106          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5107          * (could also be the top-right close button) and the text that was entered will be passed as the two
5108          * parameters to the callback.
5109          * @param {String} title The title bar text
5110          * @param {String} msg The message box body text
5111          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5112          * @param {Object} scope (optional) The scope of the callback function
5113          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5114          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5115          * @return {Roo.MessageBox} This message box
5116          */
5117         prompt : function(title, msg, fn, scope, multiline){
5118             this.show({
5119                 title : title,
5120                 msg : msg,
5121                 buttons: this.OKCANCEL,
5122                 fn: fn,
5123                 minWidth:250,
5124                 scope : scope,
5125                 prompt:true,
5126                 multiline: multiline,
5127                 modal : true
5128             });
5129             return this;
5130         },
5131
5132         /**
5133          * Button config that displays a single OK button
5134          * @type Object
5135          */
5136         OK : {ok:true},
5137         /**
5138          * Button config that displays Yes and No buttons
5139          * @type Object
5140          */
5141         YESNO : {yes:true, no:true},
5142         /**
5143          * Button config that displays OK and Cancel buttons
5144          * @type Object
5145          */
5146         OKCANCEL : {ok:true, cancel:true},
5147         /**
5148          * Button config that displays Yes, No and Cancel buttons
5149          * @type Object
5150          */
5151         YESNOCANCEL : {yes:true, no:true, cancel:true},
5152
5153         /**
5154          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5155          * @type Number
5156          */
5157         defaultTextHeight : 75,
5158         /**
5159          * The maximum width in pixels of the message box (defaults to 600)
5160          * @type Number
5161          */
5162         maxWidth : 600,
5163         /**
5164          * The minimum width in pixels of the message box (defaults to 100)
5165          * @type Number
5166          */
5167         minWidth : 100,
5168         /**
5169          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5170          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5171          * @type Number
5172          */
5173         minProgressWidth : 250,
5174         /**
5175          * An object containing the default button text strings that can be overriden for localized language support.
5176          * Supported properties are: ok, cancel, yes and no.
5177          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5178          * @type Object
5179          */
5180         buttonText : {
5181             ok : "OK",
5182             cancel : "Cancel",
5183             yes : "Yes",
5184             no : "No"
5185         }
5186     };
5187 }();
5188
5189 /**
5190  * Shorthand for {@link Roo.MessageBox}
5191  */
5192 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5193 Roo.Msg = Roo.Msg || Roo.MessageBox;
5194 /*
5195  * - LGPL
5196  *
5197  * navbar
5198  * 
5199  */
5200
5201 /**
5202  * @class Roo.bootstrap.Navbar
5203  * @extends Roo.bootstrap.Component
5204  * Bootstrap Navbar class
5205
5206  * @constructor
5207  * Create a new Navbar
5208  * @param {Object} config The config object
5209  */
5210
5211
5212 Roo.bootstrap.Navbar = function(config){
5213     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5214     this.addEvents({
5215         // raw events
5216         /**
5217          * @event beforetoggle
5218          * Fire before toggle the menu
5219          * @param {Roo.EventObject} e
5220          */
5221         "beforetoggle" : true
5222     });
5223 };
5224
5225 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5226     
5227     
5228    
5229     // private
5230     navItems : false,
5231     loadMask : false,
5232     
5233     
5234     getAutoCreate : function(){
5235         
5236         
5237         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5238         
5239     },
5240     
5241     initEvents :function ()
5242     {
5243         //Roo.log(this.el.select('.navbar-toggle',true));
5244         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5245         
5246         var mark = {
5247             tag: "div",
5248             cls:"x-dlg-mask"
5249         };
5250         
5251         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5252         
5253         var size = this.el.getSize();
5254         this.maskEl.setSize(size.width, size.height);
5255         this.maskEl.enableDisplayMode("block");
5256         this.maskEl.hide();
5257         
5258         if(this.loadMask){
5259             this.maskEl.show();
5260         }
5261     },
5262     
5263     
5264     getChildContainer : function()
5265     {
5266         if (this.el && this.el.select('.collapse').getCount()) {
5267             return this.el.select('.collapse',true).first();
5268         }
5269         
5270         return this.el;
5271     },
5272     
5273     mask : function()
5274     {
5275         this.maskEl.show();
5276     },
5277     
5278     unmask : function()
5279     {
5280         this.maskEl.hide();
5281     },
5282     onToggle : function()
5283     {
5284         
5285         if(this.fireEvent('beforetoggle', this) === false){
5286             return;
5287         }
5288         var ce = this.el.select('.navbar-collapse',true).first();
5289       
5290         if (!ce.hasClass('show')) {
5291            this.expand();
5292         } else {
5293             this.collapse();
5294         }
5295         
5296         
5297     
5298     },
5299     /**
5300      * Expand the navbar pulldown 
5301      */
5302     expand : function ()
5303     {
5304        
5305         var ce = this.el.select('.navbar-collapse',true).first();
5306         if (ce.hasClass('collapsing')) {
5307             return;
5308         }
5309         ce.dom.style.height = '';
5310                // show it...
5311         ce.addClass('in'); // old...
5312         ce.removeClass('collapse');
5313         ce.addClass('show');
5314         var h = ce.getHeight();
5315         Roo.log(h);
5316         ce.removeClass('show');
5317         // at this point we should be able to see it..
5318         ce.addClass('collapsing');
5319         
5320         ce.setHeight(0); // resize it ...
5321         ce.on('transitionend', function() {
5322             //Roo.log('done transition');
5323             ce.removeClass('collapsing');
5324             ce.addClass('show');
5325             ce.removeClass('collapse');
5326
5327             ce.dom.style.height = '';
5328         }, this, { single: true} );
5329         ce.setHeight(h);
5330         ce.dom.scrollTop = 0;
5331     },
5332     /**
5333      * Collapse the navbar pulldown 
5334      */
5335     collapse : function()
5336     {
5337          var ce = this.el.select('.navbar-collapse',true).first();
5338        
5339         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5340             // it's collapsed or collapsing..
5341             return;
5342         }
5343         ce.removeClass('in'); // old...
5344         ce.setHeight(ce.getHeight());
5345         ce.removeClass('show');
5346         ce.addClass('collapsing');
5347         
5348         ce.on('transitionend', function() {
5349             ce.dom.style.height = '';
5350             ce.removeClass('collapsing');
5351             ce.addClass('collapse');
5352         }, this, { single: true} );
5353         ce.setHeight(0);
5354     }
5355     
5356     
5357     
5358 });
5359
5360
5361
5362  
5363
5364  /*
5365  * - LGPL
5366  *
5367  * navbar
5368  * 
5369  */
5370
5371 /**
5372  * @class Roo.bootstrap.NavSimplebar
5373  * @extends Roo.bootstrap.Navbar
5374  * Bootstrap Sidebar class
5375  *
5376  * @cfg {Boolean} inverse is inverted color
5377  * 
5378  * @cfg {String} type (nav | pills | tabs)
5379  * @cfg {Boolean} arrangement stacked | justified
5380  * @cfg {String} align (left | right) alignment
5381  * 
5382  * @cfg {Boolean} main (true|false) main nav bar? default false
5383  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5384  * 
5385  * @cfg {String} tag (header|footer|nav|div) default is nav 
5386
5387  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5388  * 
5389  * 
5390  * @constructor
5391  * Create a new Sidebar
5392  * @param {Object} config The config object
5393  */
5394
5395
5396 Roo.bootstrap.NavSimplebar = function(config){
5397     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5398 };
5399
5400 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5401     
5402     inverse: false,
5403     
5404     type: false,
5405     arrangement: '',
5406     align : false,
5407     
5408     weight : 'light',
5409     
5410     main : false,
5411     
5412     
5413     tag : false,
5414     
5415     
5416     getAutoCreate : function(){
5417         
5418         
5419         var cfg = {
5420             tag : this.tag || 'div',
5421             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5422         };
5423         if (['light','white'].indexOf(this.weight) > -1) {
5424             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5425         }
5426         cfg.cls += ' bg-' + this.weight;
5427         
5428         if (this.inverse) {
5429             cfg.cls += ' navbar-inverse';
5430             
5431         }
5432         
5433         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5434         
5435         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5436             return cfg;
5437         }
5438         
5439         
5440     
5441         
5442         cfg.cn = [
5443             {
5444                 cls: 'nav nav-' + this.xtype,
5445                 tag : 'ul'
5446             }
5447         ];
5448         
5449          
5450         this.type = this.type || 'nav';
5451         if (['tabs','pills'].indexOf(this.type) != -1) {
5452             cfg.cn[0].cls += ' nav-' + this.type
5453         
5454         
5455         } else {
5456             if (this.type!=='nav') {
5457                 Roo.log('nav type must be nav/tabs/pills')
5458             }
5459             cfg.cn[0].cls += ' navbar-nav'
5460         }
5461         
5462         
5463         
5464         
5465         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5466             cfg.cn[0].cls += ' nav-' + this.arrangement;
5467         }
5468         
5469         
5470         if (this.align === 'right') {
5471             cfg.cn[0].cls += ' navbar-right';
5472         }
5473         
5474         
5475         
5476         
5477         return cfg;
5478     
5479         
5480     }
5481     
5482     
5483     
5484 });
5485
5486
5487
5488  
5489
5490  
5491        /*
5492  * - LGPL
5493  *
5494  * navbar
5495  * navbar-fixed-top
5496  * navbar-expand-md  fixed-top 
5497  */
5498
5499 /**
5500  * @class Roo.bootstrap.NavHeaderbar
5501  * @extends Roo.bootstrap.NavSimplebar
5502  * Bootstrap Sidebar class
5503  *
5504  * @cfg {String} brand what is brand
5505  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5506  * @cfg {String} brand_href href of the brand
5507  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5508  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5509  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5510  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5511  * 
5512  * @constructor
5513  * Create a new Sidebar
5514  * @param {Object} config The config object
5515  */
5516
5517
5518 Roo.bootstrap.NavHeaderbar = function(config){
5519     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5520       
5521 };
5522
5523 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5524     
5525     position: '',
5526     brand: '',
5527     brand_href: false,
5528     srButton : true,
5529     autohide : false,
5530     desktopCenter : false,
5531    
5532     
5533     getAutoCreate : function(){
5534         
5535         var   cfg = {
5536             tag: this.nav || 'nav',
5537             cls: 'navbar navbar-expand-md',
5538             role: 'navigation',
5539             cn: []
5540         };
5541         
5542         var cn = cfg.cn;
5543         if (this.desktopCenter) {
5544             cn.push({cls : 'container', cn : []});
5545             cn = cn[0].cn;
5546         }
5547         
5548         if(this.srButton){
5549             var btn = {
5550                 tag: 'button',
5551                 type: 'button',
5552                 cls: 'navbar-toggle navbar-toggler',
5553                 'data-toggle': 'collapse',
5554                 cn: [
5555                     {
5556                         tag: 'span',
5557                         cls: 'sr-only',
5558                         html: 'Toggle navigation'
5559                     },
5560                     {
5561                         tag: 'span',
5562                         cls: 'icon-bar navbar-toggler-icon'
5563                     },
5564                     {
5565                         tag: 'span',
5566                         cls: 'icon-bar'
5567                     },
5568                     {
5569                         tag: 'span',
5570                         cls: 'icon-bar'
5571                     }
5572                 ]
5573             };
5574             
5575             cn.push( Roo.bootstrap.version == 4 ? btn : {
5576                 tag: 'div',
5577                 cls: 'navbar-header',
5578                 cn: [
5579                     btn
5580                 ]
5581             });
5582         }
5583         
5584         cn.push({
5585             tag: 'div',
5586             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5587             cn : []
5588         });
5589         
5590         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5591         
5592         if (['light','white'].indexOf(this.weight) > -1) {
5593             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5594         }
5595         cfg.cls += ' bg-' + this.weight;
5596         
5597         
5598         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5599             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5600             
5601             // tag can override this..
5602             
5603             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5604         }
5605         
5606         if (this.brand !== '') {
5607             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5608             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5609                 tag: 'a',
5610                 href: this.brand_href ? this.brand_href : '#',
5611                 cls: 'navbar-brand',
5612                 cn: [
5613                 this.brand
5614                 ]
5615             });
5616         }
5617         
5618         if(this.main){
5619             cfg.cls += ' main-nav';
5620         }
5621         
5622         
5623         return cfg;
5624
5625         
5626     },
5627     getHeaderChildContainer : function()
5628     {
5629         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5630             return this.el.select('.navbar-header',true).first();
5631         }
5632         
5633         return this.getChildContainer();
5634     },
5635     
5636     getChildContainer : function()
5637     {
5638          
5639         return this.el.select('.roo-navbar-collapse',true).first();
5640          
5641         
5642     },
5643     
5644     initEvents : function()
5645     {
5646         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5647         
5648         if (this.autohide) {
5649             
5650             var prevScroll = 0;
5651             var ft = this.el;
5652             
5653             Roo.get(document).on('scroll',function(e) {
5654                 var ns = Roo.get(document).getScroll().top;
5655                 var os = prevScroll;
5656                 prevScroll = ns;
5657                 
5658                 if(ns > os){
5659                     ft.removeClass('slideDown');
5660                     ft.addClass('slideUp');
5661                     return;
5662                 }
5663                 ft.removeClass('slideUp');
5664                 ft.addClass('slideDown');
5665                  
5666               
5667           },this);
5668         }
5669     }    
5670     
5671 });
5672
5673
5674
5675  
5676
5677  /*
5678  * - LGPL
5679  *
5680  * navbar
5681  * 
5682  */
5683
5684 /**
5685  * @class Roo.bootstrap.NavSidebar
5686  * @extends Roo.bootstrap.Navbar
5687  * Bootstrap Sidebar class
5688  * 
5689  * @constructor
5690  * Create a new Sidebar
5691  * @param {Object} config The config object
5692  */
5693
5694
5695 Roo.bootstrap.NavSidebar = function(config){
5696     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5697 };
5698
5699 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5700     
5701     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5702     
5703     getAutoCreate : function(){
5704         
5705         
5706         return  {
5707             tag: 'div',
5708             cls: 'sidebar sidebar-nav'
5709         };
5710     
5711         
5712     }
5713     
5714     
5715     
5716 });
5717
5718
5719
5720  
5721
5722  /*
5723  * - LGPL
5724  *
5725  * nav group
5726  * 
5727  */
5728
5729 /**
5730  * @class Roo.bootstrap.NavGroup
5731  * @extends Roo.bootstrap.Component
5732  * Bootstrap NavGroup class
5733  * @cfg {String} align (left|right)
5734  * @cfg {Boolean} inverse
5735  * @cfg {String} type (nav|pills|tab) default nav
5736  * @cfg {String} navId - reference Id for navbar.
5737
5738  * 
5739  * @constructor
5740  * Create a new nav group
5741  * @param {Object} config The config object
5742  */
5743
5744 Roo.bootstrap.NavGroup = function(config){
5745     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5746     this.navItems = [];
5747    
5748     Roo.bootstrap.NavGroup.register(this);
5749      this.addEvents({
5750         /**
5751              * @event changed
5752              * Fires when the active item changes
5753              * @param {Roo.bootstrap.NavGroup} this
5754              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5755              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
5756          */
5757         'changed': true
5758      });
5759     
5760 };
5761
5762 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
5763     
5764     align: '',
5765     inverse: false,
5766     form: false,
5767     type: 'nav',
5768     navId : '',
5769     // private
5770     
5771     navItems : false, 
5772     
5773     getAutoCreate : function()
5774     {
5775         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5776         
5777         cfg = {
5778             tag : 'ul',
5779             cls: 'nav' 
5780         };
5781         if (Roo.bootstrap.version == 4) {
5782             if (['tabs','pills'].indexOf(this.type) != -1) {
5783                 cfg.cls += ' nav-' + this.type; 
5784             } else {
5785                 // trying to remove so header bar can right align top?
5786                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5787                     // do not use on header bar... 
5788                     cfg.cls += ' navbar-nav';
5789                 }
5790             }
5791             
5792         } else {
5793             if (['tabs','pills'].indexOf(this.type) != -1) {
5794                 cfg.cls += ' nav-' + this.type
5795             } else {
5796                 if (this.type !== 'nav') {
5797                     Roo.log('nav type must be nav/tabs/pills')
5798                 }
5799                 cfg.cls += ' navbar-nav'
5800             }
5801         }
5802         
5803         if (this.parent() && this.parent().sidebar) {
5804             cfg = {
5805                 tag: 'ul',
5806                 cls: 'dashboard-menu sidebar-menu'
5807             };
5808             
5809             return cfg;
5810         }
5811         
5812         if (this.form === true) {
5813             cfg = {
5814                 tag: 'form',
5815                 cls: 'navbar-form form-inline'
5816             };
5817             //nav navbar-right ml-md-auto
5818             if (this.align === 'right') {
5819                 cfg.cls += ' navbar-right ml-md-auto';
5820             } else {
5821                 cfg.cls += ' navbar-left';
5822             }
5823         }
5824         
5825         if (this.align === 'right') {
5826             cfg.cls += ' navbar-right ml-md-auto';
5827         } else {
5828             cfg.cls += ' mr-auto';
5829         }
5830         
5831         if (this.inverse) {
5832             cfg.cls += ' navbar-inverse';
5833             
5834         }
5835         
5836         
5837         return cfg;
5838     },
5839     /**
5840     * sets the active Navigation item
5841     * @param {Roo.bootstrap.NavItem} the new current navitem
5842     */
5843     setActiveItem : function(item)
5844     {
5845         var prev = false;
5846         Roo.each(this.navItems, function(v){
5847             if (v == item) {
5848                 return ;
5849             }
5850             if (v.isActive()) {
5851                 v.setActive(false, true);
5852                 prev = v;
5853                 
5854             }
5855             
5856         });
5857
5858         item.setActive(true, true);
5859         this.fireEvent('changed', this, item, prev);
5860         
5861         
5862     },
5863     /**
5864     * gets the active Navigation item
5865     * @return {Roo.bootstrap.NavItem} the current navitem
5866     */
5867     getActive : function()
5868     {
5869         
5870         var prev = false;
5871         Roo.each(this.navItems, function(v){
5872             
5873             if (v.isActive()) {
5874                 prev = v;
5875                 
5876             }
5877             
5878         });
5879         return prev;
5880     },
5881     
5882     indexOfNav : function()
5883     {
5884         
5885         var prev = false;
5886         Roo.each(this.navItems, function(v,i){
5887             
5888             if (v.isActive()) {
5889                 prev = i;
5890                 
5891             }
5892             
5893         });
5894         return prev;
5895     },
5896     /**
5897     * adds a Navigation item
5898     * @param {Roo.bootstrap.NavItem} the navitem to add
5899     */
5900     addItem : function(cfg)
5901     {
5902         if (this.form && Roo.bootstrap.version == 4) {
5903             cfg.tag = 'div';
5904         }
5905         var cn = new Roo.bootstrap.NavItem(cfg);
5906         this.register(cn);
5907         cn.parentId = this.id;
5908         cn.onRender(this.el, null);
5909         return cn;
5910     },
5911     /**
5912     * register a Navigation item
5913     * @param {Roo.bootstrap.NavItem} the navitem to add
5914     */
5915     register : function(item)
5916     {
5917         this.navItems.push( item);
5918         item.navId = this.navId;
5919     
5920     },
5921     
5922     /**
5923     * clear all the Navigation item
5924     */
5925    
5926     clearAll : function()
5927     {
5928         this.navItems = [];
5929         this.el.dom.innerHTML = '';
5930     },
5931     
5932     getNavItem: function(tabId)
5933     {
5934         var ret = false;
5935         Roo.each(this.navItems, function(e) {
5936             if (e.tabId == tabId) {
5937                ret =  e;
5938                return false;
5939             }
5940             return true;
5941             
5942         });
5943         return ret;
5944     },
5945     
5946     setActiveNext : function()
5947     {
5948         var i = this.indexOfNav(this.getActive());
5949         if (i > this.navItems.length) {
5950             return;
5951         }
5952         this.setActiveItem(this.navItems[i+1]);
5953     },
5954     setActivePrev : function()
5955     {
5956         var i = this.indexOfNav(this.getActive());
5957         if (i  < 1) {
5958             return;
5959         }
5960         this.setActiveItem(this.navItems[i-1]);
5961     },
5962     clearWasActive : function(except) {
5963         Roo.each(this.navItems, function(e) {
5964             if (e.tabId != except.tabId && e.was_active) {
5965                e.was_active = false;
5966                return false;
5967             }
5968             return true;
5969             
5970         });
5971     },
5972     getWasActive : function ()
5973     {
5974         var r = false;
5975         Roo.each(this.navItems, function(e) {
5976             if (e.was_active) {
5977                r = e;
5978                return false;
5979             }
5980             return true;
5981             
5982         });
5983         return r;
5984     }
5985     
5986     
5987 });
5988
5989  
5990 Roo.apply(Roo.bootstrap.NavGroup, {
5991     
5992     groups: {},
5993      /**
5994     * register a Navigation Group
5995     * @param {Roo.bootstrap.NavGroup} the navgroup to add
5996     */
5997     register : function(navgrp)
5998     {
5999         this.groups[navgrp.navId] = navgrp;
6000         
6001     },
6002     /**
6003     * fetch a Navigation Group based on the navigation ID
6004     * @param {string} the navgroup to add
6005     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6006     */
6007     get: function(navId) {
6008         if (typeof(this.groups[navId]) == 'undefined') {
6009             return false;
6010             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6011         }
6012         return this.groups[navId] ;
6013     }
6014     
6015     
6016     
6017 });
6018
6019  /*
6020  * - LGPL
6021  *
6022  * row
6023  * 
6024  */
6025
6026 /**
6027  * @class Roo.bootstrap.NavItem
6028  * @extends Roo.bootstrap.Component
6029  * Bootstrap Navbar.NavItem class
6030  * @cfg {String} href  link to
6031  * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
6032
6033  * @cfg {String} html content of button
6034  * @cfg {String} badge text inside badge
6035  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6036  * @cfg {String} glyphicon DEPRICATED - use fa
6037  * @cfg {String} icon DEPRICATED - use fa
6038  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6039  * @cfg {Boolean} active Is item active
6040  * @cfg {Boolean} disabled Is item disabled
6041  
6042  * @cfg {Boolean} preventDefault (true | false) default false
6043  * @cfg {String} tabId the tab that this item activates.
6044  * @cfg {String} tagtype (a|span) render as a href or span?
6045  * @cfg {Boolean} animateRef (true|false) link to element default false  
6046   
6047  * @constructor
6048  * Create a new Navbar Item
6049  * @param {Object} config The config object
6050  */
6051 Roo.bootstrap.NavItem = function(config){
6052     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6053     this.addEvents({
6054         // raw events
6055         /**
6056          * @event click
6057          * The raw click event for the entire grid.
6058          * @param {Roo.EventObject} e
6059          */
6060         "click" : true,
6061          /**
6062             * @event changed
6063             * Fires when the active item active state changes
6064             * @param {Roo.bootstrap.NavItem} this
6065             * @param {boolean} state the new state
6066              
6067          */
6068         'changed': true,
6069         /**
6070             * @event scrollto
6071             * Fires when scroll to element
6072             * @param {Roo.bootstrap.NavItem} this
6073             * @param {Object} options
6074             * @param {Roo.EventObject} e
6075              
6076          */
6077         'scrollto': true
6078     });
6079    
6080 };
6081
6082 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6083     
6084     href: false,
6085     html: '',
6086     badge: '',
6087     icon: false,
6088     fa : false,
6089     glyphicon: false,
6090     active: false,
6091     preventDefault : false,
6092     tabId : false,
6093     tagtype : 'a',
6094     tag: 'li',
6095     disabled : false,
6096     animateRef : false,
6097     was_active : false,
6098     button_weight : '',
6099     button_outline : false,
6100     
6101     navLink: false,
6102     
6103     getAutoCreate : function(){
6104          
6105         var cfg = {
6106             tag: this.tag,
6107             cls: 'nav-item'
6108         };
6109         
6110         if (this.active) {
6111             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
6112         }
6113         if (this.disabled) {
6114             cfg.cls += ' disabled';
6115         }
6116         
6117         // BS4 only?
6118         if (this.button_weight.length) {
6119             cfg.tag = this.href ? 'a' : 'button';
6120             cfg.html = this.html || '';
6121             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6122             if (this.href) {
6123                 cfg.href = this.href;
6124             }
6125             if (this.fa) {
6126                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6127             }
6128             
6129             // menu .. should add dropdown-menu class - so no need for carat..
6130             
6131             if (this.badge !== '') {
6132                  
6133                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6134             }
6135             return cfg;
6136         }
6137         
6138         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6139             cfg.cn = [
6140                 {
6141                     tag: this.tagtype,
6142                     href : this.href || "#",
6143                     html: this.html || ''
6144                 }
6145             ];
6146             if (this.tagtype == 'a') {
6147                 cfg.cn[0].cls = 'nav-link';
6148             }
6149             if (this.icon) {
6150                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6151             }
6152             if (this.fa) {
6153                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6154             }
6155             if(this.glyphicon) {
6156                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6157             }
6158             
6159             if (this.menu) {
6160                 
6161                 cfg.cn[0].html += " <span class='caret'></span>";
6162              
6163             }
6164             
6165             if (this.badge !== '') {
6166                  
6167                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6168             }
6169         }
6170         
6171         
6172         
6173         return cfg;
6174     },
6175     onRender : function(ct, position)
6176     {
6177        // Roo.log("Call onRender: " + this.xtype);
6178         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6179             this.tag = 'div';
6180         }
6181         
6182         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6183         this.navLink = this.el.select('.nav-link',true).first();
6184         return ret;
6185     },
6186       
6187     
6188     initEvents: function() 
6189     {
6190         if (typeof (this.menu) != 'undefined') {
6191             this.menu.parentType = this.xtype;
6192             this.menu.triggerEl = this.el;
6193             this.menu = this.addxtype(Roo.apply({}, this.menu));
6194         }
6195         
6196         this.el.select('a',true).on('click', this.onClick, this);
6197         
6198         if(this.tagtype == 'span'){
6199             this.el.select('span',true).on('click', this.onClick, this);
6200         }
6201        
6202         // at this point parent should be available..
6203         this.parent().register(this);
6204     },
6205     
6206     onClick : function(e)
6207     {
6208         if (e.getTarget('.dropdown-menu-item')) {
6209             // did you click on a menu itemm.... - then don't trigger onclick..
6210             return;
6211         }
6212         
6213         if(
6214                 this.preventDefault || 
6215                 this.href == '#' 
6216         ){
6217             Roo.log("NavItem - prevent Default?");
6218             e.preventDefault();
6219         }
6220         
6221         if (this.disabled) {
6222             return;
6223         }
6224         
6225         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6226         if (tg && tg.transition) {
6227             Roo.log("waiting for the transitionend");
6228             return;
6229         }
6230         
6231         
6232         
6233         //Roo.log("fire event clicked");
6234         if(this.fireEvent('click', this, e) === false){
6235             return;
6236         };
6237         
6238         if(this.tagtype == 'span'){
6239             return;
6240         }
6241         
6242         //Roo.log(this.href);
6243         var ael = this.el.select('a',true).first();
6244         //Roo.log(ael);
6245         
6246         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6247             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6248             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6249                 return; // ignore... - it's a 'hash' to another page.
6250             }
6251             Roo.log("NavItem - prevent Default?");
6252             e.preventDefault();
6253             this.scrollToElement(e);
6254         }
6255         
6256         
6257         var p =  this.parent();
6258    
6259         if (['tabs','pills'].indexOf(p.type)!==-1) {
6260             if (typeof(p.setActiveItem) !== 'undefined') {
6261                 p.setActiveItem(this);
6262             }
6263         }
6264         
6265         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6266         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6267             // remove the collapsed menu expand...
6268             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6269         }
6270     },
6271     
6272     isActive: function () {
6273         return this.active
6274     },
6275     setActive : function(state, fire, is_was_active)
6276     {
6277         if (this.active && !state && this.navId) {
6278             this.was_active = true;
6279             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6280             if (nv) {
6281                 nv.clearWasActive(this);
6282             }
6283             
6284         }
6285         this.active = state;
6286         
6287         if (!state ) {
6288             this.el.removeClass('active');
6289             this.navLink ? this.navLink.removeClass('active') : false;
6290         } else if (!this.el.hasClass('active')) {
6291             
6292             this.el.addClass('active');
6293             if (Roo.bootstrap.version == 4 && this.navLink ) {
6294                 this.navLink.addClass('active');
6295             }
6296             
6297         }
6298         if (fire) {
6299             this.fireEvent('changed', this, state);
6300         }
6301         
6302         // show a panel if it's registered and related..
6303         
6304         if (!this.navId || !this.tabId || !state || is_was_active) {
6305             return;
6306         }
6307         
6308         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6309         if (!tg) {
6310             return;
6311         }
6312         var pan = tg.getPanelByName(this.tabId);
6313         if (!pan) {
6314             return;
6315         }
6316         // if we can not flip to new panel - go back to old nav highlight..
6317         if (false == tg.showPanel(pan)) {
6318             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6319             if (nv) {
6320                 var onav = nv.getWasActive();
6321                 if (onav) {
6322                     onav.setActive(true, false, true);
6323                 }
6324             }
6325             
6326         }
6327         
6328         
6329         
6330     },
6331      // this should not be here...
6332     setDisabled : function(state)
6333     {
6334         this.disabled = state;
6335         if (!state ) {
6336             this.el.removeClass('disabled');
6337         } else if (!this.el.hasClass('disabled')) {
6338             this.el.addClass('disabled');
6339         }
6340         
6341     },
6342     
6343     /**
6344      * Fetch the element to display the tooltip on.
6345      * @return {Roo.Element} defaults to this.el
6346      */
6347     tooltipEl : function()
6348     {
6349         return this.el.select('' + this.tagtype + '', true).first();
6350     },
6351     
6352     scrollToElement : function(e)
6353     {
6354         var c = document.body;
6355         
6356         /*
6357          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6358          */
6359         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6360             c = document.documentElement;
6361         }
6362         
6363         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6364         
6365         if(!target){
6366             return;
6367         }
6368
6369         var o = target.calcOffsetsTo(c);
6370         
6371         var options = {
6372             target : target,
6373             value : o[1]
6374         };
6375         
6376         this.fireEvent('scrollto', this, options, e);
6377         
6378         Roo.get(c).scrollTo('top', options.value, true);
6379         
6380         return;
6381     }
6382 });
6383  
6384
6385  /*
6386  * - LGPL
6387  *
6388  * sidebar item
6389  *
6390  *  li
6391  *    <span> icon </span>
6392  *    <span> text </span>
6393  *    <span>badge </span>
6394  */
6395
6396 /**
6397  * @class Roo.bootstrap.NavSidebarItem
6398  * @extends Roo.bootstrap.NavItem
6399  * Bootstrap Navbar.NavSidebarItem class
6400  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6401  * {Boolean} open is the menu open
6402  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6403  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6404  * {String} buttonSize (sm|md|lg)the extra classes for the button
6405  * {Boolean} showArrow show arrow next to the text (default true)
6406  * @constructor
6407  * Create a new Navbar Button
6408  * @param {Object} config The config object
6409  */
6410 Roo.bootstrap.NavSidebarItem = function(config){
6411     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6412     this.addEvents({
6413         // raw events
6414         /**
6415          * @event click
6416          * The raw click event for the entire grid.
6417          * @param {Roo.EventObject} e
6418          */
6419         "click" : true,
6420          /**
6421             * @event changed
6422             * Fires when the active item active state changes
6423             * @param {Roo.bootstrap.NavSidebarItem} this
6424             * @param {boolean} state the new state
6425              
6426          */
6427         'changed': true
6428     });
6429    
6430 };
6431
6432 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6433     
6434     badgeWeight : 'default',
6435     
6436     open: false,
6437     
6438     buttonView : false,
6439     
6440     buttonWeight : 'default',
6441     
6442     buttonSize : 'md',
6443     
6444     showArrow : true,
6445     
6446     getAutoCreate : function(){
6447         
6448         
6449         var a = {
6450                 tag: 'a',
6451                 href : this.href || '#',
6452                 cls: '',
6453                 html : '',
6454                 cn : []
6455         };
6456         
6457         if(this.buttonView){
6458             a = {
6459                 tag: 'button',
6460                 href : this.href || '#',
6461                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6462                 html : this.html,
6463                 cn : []
6464             };
6465         }
6466         
6467         var cfg = {
6468             tag: 'li',
6469             cls: '',
6470             cn: [ a ]
6471         };
6472         
6473         if (this.active) {
6474             cfg.cls += ' active';
6475         }
6476         
6477         if (this.disabled) {
6478             cfg.cls += ' disabled';
6479         }
6480         if (this.open) {
6481             cfg.cls += ' open x-open';
6482         }
6483         // left icon..
6484         if (this.glyphicon || this.icon) {
6485             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6486             a.cn.push({ tag : 'i', cls : c }) ;
6487         }
6488         
6489         if(!this.buttonView){
6490             var span = {
6491                 tag: 'span',
6492                 html : this.html || ''
6493             };
6494
6495             a.cn.push(span);
6496             
6497         }
6498         
6499         if (this.badge !== '') {
6500             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6501         }
6502         
6503         if (this.menu) {
6504             
6505             if(this.showArrow){
6506                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6507             }
6508             
6509             a.cls += ' dropdown-toggle treeview' ;
6510         }
6511         
6512         return cfg;
6513     },
6514     
6515     initEvents : function()
6516     { 
6517         if (typeof (this.menu) != 'undefined') {
6518             this.menu.parentType = this.xtype;
6519             this.menu.triggerEl = this.el;
6520             this.menu = this.addxtype(Roo.apply({}, this.menu));
6521         }
6522         
6523         this.el.on('click', this.onClick, this);
6524         
6525         if(this.badge !== ''){
6526             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6527         }
6528         
6529     },
6530     
6531     onClick : function(e)
6532     {
6533         if(this.disabled){
6534             e.preventDefault();
6535             return;
6536         }
6537         
6538         if(this.preventDefault){
6539             e.preventDefault();
6540         }
6541         
6542         this.fireEvent('click', this, e);
6543     },
6544     
6545     disable : function()
6546     {
6547         this.setDisabled(true);
6548     },
6549     
6550     enable : function()
6551     {
6552         this.setDisabled(false);
6553     },
6554     
6555     setDisabled : function(state)
6556     {
6557         if(this.disabled == state){
6558             return;
6559         }
6560         
6561         this.disabled = state;
6562         
6563         if (state) {
6564             this.el.addClass('disabled');
6565             return;
6566         }
6567         
6568         this.el.removeClass('disabled');
6569         
6570         return;
6571     },
6572     
6573     setActive : function(state)
6574     {
6575         if(this.active == state){
6576             return;
6577         }
6578         
6579         this.active = state;
6580         
6581         if (state) {
6582             this.el.addClass('active');
6583             return;
6584         }
6585         
6586         this.el.removeClass('active');
6587         
6588         return;
6589     },
6590     
6591     isActive: function () 
6592     {
6593         return this.active;
6594     },
6595     
6596     setBadge : function(str)
6597     {
6598         if(!this.badgeEl){
6599             return;
6600         }
6601         
6602         this.badgeEl.dom.innerHTML = str;
6603     }
6604     
6605    
6606      
6607  
6608 });
6609  
6610
6611  /*
6612  * - LGPL
6613  *
6614  *  Breadcrumb Nav
6615  * 
6616  */
6617 Roo.namespace('Roo.bootstrap.breadcrumb');
6618
6619
6620 /**
6621  * @class Roo.bootstrap.breadcrumb.Nav
6622  * @extends Roo.bootstrap.Component
6623  * Bootstrap Breadcrumb Nav Class
6624  *  
6625  * @children Roo.bootstrap.breadcrumb.Item
6626  * 
6627  * @constructor
6628  * Create a new breadcrumb.Nav
6629  * @param {Object} config The config object
6630  */
6631
6632
6633 Roo.bootstrap.breadcrumb.Nav = function(config){
6634     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6635     
6636     
6637 };
6638
6639 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6640     
6641     getAutoCreate : function()
6642     {
6643
6644         var cfg = {
6645             tag: 'nav',
6646             cn : [
6647                 {
6648                     tag : 'ol',
6649                     cls : 'breadcrumb'
6650                 }
6651             ]
6652             
6653         };
6654           
6655         return cfg;
6656     },
6657     
6658     initEvents: function()
6659     {
6660         this.olEl = this.el.select('ol',true).first();    
6661     },
6662     getChildContainer : function()
6663     {
6664         return this.olEl;  
6665     }
6666     
6667 });
6668
6669  /*
6670  * - LGPL
6671  *
6672  *  Breadcrumb Item
6673  * 
6674  */
6675
6676
6677 /**
6678  * @class Roo.bootstrap.breadcrumb.Nav
6679  * @extends Roo.bootstrap.Component
6680  * Bootstrap Breadcrumb Nav Class
6681  *  
6682  * @children Roo.bootstrap.breadcrumb.Component
6683  * @cfg {String} html the content of the link.
6684  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6685  * @cfg {Boolean} active is it active
6686
6687  * 
6688  * @constructor
6689  * Create a new breadcrumb.Nav
6690  * @param {Object} config The config object
6691  */
6692
6693 Roo.bootstrap.breadcrumb.Item = function(config){
6694     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6695     this.addEvents({
6696         // img events
6697         /**
6698          * @event click
6699          * The img click event for the img.
6700          * @param {Roo.EventObject} e
6701          */
6702         "click" : true
6703     });
6704     
6705 };
6706
6707 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
6708     
6709     href: false,
6710     html : '',
6711     
6712     getAutoCreate : function()
6713     {
6714
6715         var cfg = {
6716             tag: 'li',
6717             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6718         };
6719         if (this.href !== false) {
6720             cfg.cn = [{
6721                 tag : 'a',
6722                 href : this.href,
6723                 html : this.html
6724             }];
6725         } else {
6726             cfg.html = this.html;
6727         }
6728         
6729         return cfg;
6730     },
6731     
6732     initEvents: function()
6733     {
6734         if (this.href) {
6735             this.el.select('a', true).first().on('click',this.onClick, this)
6736         }
6737         
6738     },
6739     onClick : function(e)
6740     {
6741         e.preventDefault();
6742         this.fireEvent('click',this,  e);
6743     }
6744     
6745 });
6746
6747  /*
6748  * - LGPL
6749  *
6750  * row
6751  * 
6752  */
6753
6754 /**
6755  * @class Roo.bootstrap.Row
6756  * @extends Roo.bootstrap.Component
6757  * Bootstrap Row class (contains columns...)
6758  * 
6759  * @constructor
6760  * Create a new Row
6761  * @param {Object} config The config object
6762  */
6763
6764 Roo.bootstrap.Row = function(config){
6765     Roo.bootstrap.Row.superclass.constructor.call(this, config);
6766 };
6767
6768 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
6769     
6770     getAutoCreate : function(){
6771        return {
6772             cls: 'row clearfix'
6773        };
6774     }
6775     
6776     
6777 });
6778
6779  
6780
6781  /*
6782  * - LGPL
6783  *
6784  * pagination
6785  * 
6786  */
6787
6788 /**
6789  * @class Roo.bootstrap.Pagination
6790  * @extends Roo.bootstrap.Component
6791  * Bootstrap Pagination class
6792  * @cfg {String} size xs | sm | md | lg
6793  * @cfg {Boolean} inverse false | true
6794  * 
6795  * @constructor
6796  * Create a new Pagination
6797  * @param {Object} config The config object
6798  */
6799
6800 Roo.bootstrap.Pagination = function(config){
6801     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6802 };
6803
6804 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
6805     
6806     cls: false,
6807     size: false,
6808     inverse: false,
6809     
6810     getAutoCreate : function(){
6811         var cfg = {
6812             tag: 'ul',
6813                 cls: 'pagination'
6814         };
6815         if (this.inverse) {
6816             cfg.cls += ' inverse';
6817         }
6818         if (this.html) {
6819             cfg.html=this.html;
6820         }
6821         if (this.cls) {
6822             cfg.cls += " " + this.cls;
6823         }
6824         return cfg;
6825     }
6826    
6827 });
6828
6829  
6830
6831  /*
6832  * - LGPL
6833  *
6834  * Pagination item
6835  * 
6836  */
6837
6838
6839 /**
6840  * @class Roo.bootstrap.PaginationItem
6841  * @extends Roo.bootstrap.Component
6842  * Bootstrap PaginationItem class
6843  * @cfg {String} html text
6844  * @cfg {String} href the link
6845  * @cfg {Boolean} preventDefault (true | false) default true
6846  * @cfg {Boolean} active (true | false) default false
6847  * @cfg {Boolean} disabled default false
6848  * 
6849  * 
6850  * @constructor
6851  * Create a new PaginationItem
6852  * @param {Object} config The config object
6853  */
6854
6855
6856 Roo.bootstrap.PaginationItem = function(config){
6857     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6858     this.addEvents({
6859         // raw events
6860         /**
6861          * @event click
6862          * The raw click event for the entire grid.
6863          * @param {Roo.EventObject} e
6864          */
6865         "click" : true
6866     });
6867 };
6868
6869 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
6870     
6871     href : false,
6872     html : false,
6873     preventDefault: true,
6874     active : false,
6875     cls : false,
6876     disabled: false,
6877     
6878     getAutoCreate : function(){
6879         var cfg= {
6880             tag: 'li',
6881             cn: [
6882                 {
6883                     tag : 'a',
6884                     href : this.href ? this.href : '#',
6885                     html : this.html ? this.html : ''
6886                 }
6887             ]
6888         };
6889         
6890         if(this.cls){
6891             cfg.cls = this.cls;
6892         }
6893         
6894         if(this.disabled){
6895             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6896         }
6897         
6898         if(this.active){
6899             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6900         }
6901         
6902         return cfg;
6903     },
6904     
6905     initEvents: function() {
6906         
6907         this.el.on('click', this.onClick, this);
6908         
6909     },
6910     onClick : function(e)
6911     {
6912         Roo.log('PaginationItem on click ');
6913         if(this.preventDefault){
6914             e.preventDefault();
6915         }
6916         
6917         if(this.disabled){
6918             return;
6919         }
6920         
6921         this.fireEvent('click', this, e);
6922     }
6923    
6924 });
6925
6926  
6927
6928  /*
6929  * - LGPL
6930  *
6931  * slider
6932  * 
6933  */
6934
6935
6936 /**
6937  * @class Roo.bootstrap.Slider
6938  * @extends Roo.bootstrap.Component
6939  * Bootstrap Slider class
6940  *    
6941  * @constructor
6942  * Create a new Slider
6943  * @param {Object} config The config object
6944  */
6945
6946 Roo.bootstrap.Slider = function(config){
6947     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6948 };
6949
6950 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
6951     
6952     getAutoCreate : function(){
6953         
6954         var cfg = {
6955             tag: 'div',
6956             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6957             cn: [
6958                 {
6959                     tag: 'a',
6960                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
6961                 }
6962             ]
6963         };
6964         
6965         return cfg;
6966     }
6967    
6968 });
6969
6970  /*
6971  * Based on:
6972  * Ext JS Library 1.1.1
6973  * Copyright(c) 2006-2007, Ext JS, LLC.
6974  *
6975  * Originally Released Under LGPL - original licence link has changed is not relivant.
6976  *
6977  * Fork - LGPL
6978  * <script type="text/javascript">
6979  */
6980  
6981
6982 /**
6983  * @class Roo.grid.ColumnModel
6984  * @extends Roo.util.Observable
6985  * This is the default implementation of a ColumnModel used by the Grid. It defines
6986  * the columns in the grid.
6987  * <br>Usage:<br>
6988  <pre><code>
6989  var colModel = new Roo.grid.ColumnModel([
6990         {header: "Ticker", width: 60, sortable: true, locked: true},
6991         {header: "Company Name", width: 150, sortable: true},
6992         {header: "Market Cap.", width: 100, sortable: true},
6993         {header: "$ Sales", width: 100, sortable: true, renderer: money},
6994         {header: "Employees", width: 100, sortable: true, resizable: false}
6995  ]);
6996  </code></pre>
6997  * <p>
6998  
6999  * The config options listed for this class are options which may appear in each
7000  * individual column definition.
7001  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7002  * @constructor
7003  * @param {Object} config An Array of column config objects. See this class's
7004  * config objects for details.
7005 */
7006 Roo.grid.ColumnModel = function(config){
7007         /**
7008      * The config passed into the constructor
7009      */
7010     this.config = config;
7011     this.lookup = {};
7012
7013     // if no id, create one
7014     // if the column does not have a dataIndex mapping,
7015     // map it to the order it is in the config
7016     for(var i = 0, len = config.length; i < len; i++){
7017         var c = config[i];
7018         if(typeof c.dataIndex == "undefined"){
7019             c.dataIndex = i;
7020         }
7021         if(typeof c.renderer == "string"){
7022             c.renderer = Roo.util.Format[c.renderer];
7023         }
7024         if(typeof c.id == "undefined"){
7025             c.id = Roo.id();
7026         }
7027         if(c.editor && c.editor.xtype){
7028             c.editor  = Roo.factory(c.editor, Roo.grid);
7029         }
7030         if(c.editor && c.editor.isFormField){
7031             c.editor = new Roo.grid.GridEditor(c.editor);
7032         }
7033         this.lookup[c.id] = c;
7034     }
7035
7036     /**
7037      * The width of columns which have no width specified (defaults to 100)
7038      * @type Number
7039      */
7040     this.defaultWidth = 100;
7041
7042     /**
7043      * Default sortable of columns which have no sortable specified (defaults to false)
7044      * @type Boolean
7045      */
7046     this.defaultSortable = false;
7047
7048     this.addEvents({
7049         /**
7050              * @event widthchange
7051              * Fires when the width of a column changes.
7052              * @param {ColumnModel} this
7053              * @param {Number} columnIndex The column index
7054              * @param {Number} newWidth The new width
7055              */
7056             "widthchange": true,
7057         /**
7058              * @event headerchange
7059              * Fires when the text of a header changes.
7060              * @param {ColumnModel} this
7061              * @param {Number} columnIndex The column index
7062              * @param {Number} newText The new header text
7063              */
7064             "headerchange": true,
7065         /**
7066              * @event hiddenchange
7067              * Fires when a column is hidden or "unhidden".
7068              * @param {ColumnModel} this
7069              * @param {Number} columnIndex The column index
7070              * @param {Boolean} hidden true if hidden, false otherwise
7071              */
7072             "hiddenchange": true,
7073             /**
7074          * @event columnmoved
7075          * Fires when a column is moved.
7076          * @param {ColumnModel} this
7077          * @param {Number} oldIndex
7078          * @param {Number} newIndex
7079          */
7080         "columnmoved" : true,
7081         /**
7082          * @event columlockchange
7083          * Fires when a column's locked state is changed
7084          * @param {ColumnModel} this
7085          * @param {Number} colIndex
7086          * @param {Boolean} locked true if locked
7087          */
7088         "columnlockchange" : true
7089     });
7090     Roo.grid.ColumnModel.superclass.constructor.call(this);
7091 };
7092 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7093     /**
7094      * @cfg {String} header The header text to display in the Grid view.
7095      */
7096     /**
7097      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7098      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7099      * specified, the column's index is used as an index into the Record's data Array.
7100      */
7101     /**
7102      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7103      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7104      */
7105     /**
7106      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7107      * Defaults to the value of the {@link #defaultSortable} property.
7108      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7109      */
7110     /**
7111      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7112      */
7113     /**
7114      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7115      */
7116     /**
7117      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7118      */
7119     /**
7120      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7121      */
7122     /**
7123      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7124      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7125      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7126      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7127      */
7128        /**
7129      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7130      */
7131     /**
7132      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7133      */
7134     /**
7135      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7136      */
7137     /**
7138      * @cfg {String} cursor (Optional)
7139      */
7140     /**
7141      * @cfg {String} tooltip (Optional)
7142      */
7143     /**
7144      * @cfg {Number} xs (Optional)
7145      */
7146     /**
7147      * @cfg {Number} sm (Optional)
7148      */
7149     /**
7150      * @cfg {Number} md (Optional)
7151      */
7152     /**
7153      * @cfg {Number} lg (Optional)
7154      */
7155     /**
7156      * Returns the id of the column at the specified index.
7157      * @param {Number} index The column index
7158      * @return {String} the id
7159      */
7160     getColumnId : function(index){
7161         return this.config[index].id;
7162     },
7163
7164     /**
7165      * Returns the column for a specified id.
7166      * @param {String} id The column id
7167      * @return {Object} the column
7168      */
7169     getColumnById : function(id){
7170         return this.lookup[id];
7171     },
7172
7173     
7174     /**
7175      * Returns the column for a specified dataIndex.
7176      * @param {String} dataIndex The column dataIndex
7177      * @return {Object|Boolean} the column or false if not found
7178      */
7179     getColumnByDataIndex: function(dataIndex){
7180         var index = this.findColumnIndex(dataIndex);
7181         return index > -1 ? this.config[index] : false;
7182     },
7183     
7184     /**
7185      * Returns the index for a specified column id.
7186      * @param {String} id The column id
7187      * @return {Number} the index, or -1 if not found
7188      */
7189     getIndexById : function(id){
7190         for(var i = 0, len = this.config.length; i < len; i++){
7191             if(this.config[i].id == id){
7192                 return i;
7193             }
7194         }
7195         return -1;
7196     },
7197     
7198     /**
7199      * Returns the index for a specified column dataIndex.
7200      * @param {String} dataIndex The column dataIndex
7201      * @return {Number} the index, or -1 if not found
7202      */
7203     
7204     findColumnIndex : function(dataIndex){
7205         for(var i = 0, len = this.config.length; i < len; i++){
7206             if(this.config[i].dataIndex == dataIndex){
7207                 return i;
7208             }
7209         }
7210         return -1;
7211     },
7212     
7213     
7214     moveColumn : function(oldIndex, newIndex){
7215         var c = this.config[oldIndex];
7216         this.config.splice(oldIndex, 1);
7217         this.config.splice(newIndex, 0, c);
7218         this.dataMap = null;
7219         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7220     },
7221
7222     isLocked : function(colIndex){
7223         return this.config[colIndex].locked === true;
7224     },
7225
7226     setLocked : function(colIndex, value, suppressEvent){
7227         if(this.isLocked(colIndex) == value){
7228             return;
7229         }
7230         this.config[colIndex].locked = value;
7231         if(!suppressEvent){
7232             this.fireEvent("columnlockchange", this, colIndex, value);
7233         }
7234     },
7235
7236     getTotalLockedWidth : function(){
7237         var totalWidth = 0;
7238         for(var i = 0; i < this.config.length; i++){
7239             if(this.isLocked(i) && !this.isHidden(i)){
7240                 this.totalWidth += this.getColumnWidth(i);
7241             }
7242         }
7243         return totalWidth;
7244     },
7245
7246     getLockedCount : function(){
7247         for(var i = 0, len = this.config.length; i < len; i++){
7248             if(!this.isLocked(i)){
7249                 return i;
7250             }
7251         }
7252         
7253         return this.config.length;
7254     },
7255
7256     /**
7257      * Returns the number of columns.
7258      * @return {Number}
7259      */
7260     getColumnCount : function(visibleOnly){
7261         if(visibleOnly === true){
7262             var c = 0;
7263             for(var i = 0, len = this.config.length; i < len; i++){
7264                 if(!this.isHidden(i)){
7265                     c++;
7266                 }
7267             }
7268             return c;
7269         }
7270         return this.config.length;
7271     },
7272
7273     /**
7274      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7275      * @param {Function} fn
7276      * @param {Object} scope (optional)
7277      * @return {Array} result
7278      */
7279     getColumnsBy : function(fn, scope){
7280         var r = [];
7281         for(var i = 0, len = this.config.length; i < len; i++){
7282             var c = this.config[i];
7283             if(fn.call(scope||this, c, i) === true){
7284                 r[r.length] = c;
7285             }
7286         }
7287         return r;
7288     },
7289
7290     /**
7291      * Returns true if the specified column is sortable.
7292      * @param {Number} col The column index
7293      * @return {Boolean}
7294      */
7295     isSortable : function(col){
7296         if(typeof this.config[col].sortable == "undefined"){
7297             return this.defaultSortable;
7298         }
7299         return this.config[col].sortable;
7300     },
7301
7302     /**
7303      * Returns the rendering (formatting) function defined for the column.
7304      * @param {Number} col The column index.
7305      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7306      */
7307     getRenderer : function(col){
7308         if(!this.config[col].renderer){
7309             return Roo.grid.ColumnModel.defaultRenderer;
7310         }
7311         return this.config[col].renderer;
7312     },
7313
7314     /**
7315      * Sets the rendering (formatting) function for a column.
7316      * @param {Number} col The column index
7317      * @param {Function} fn The function to use to process the cell's raw data
7318      * to return HTML markup for the grid view. The render function is called with
7319      * the following parameters:<ul>
7320      * <li>Data value.</li>
7321      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7322      * <li>css A CSS style string to apply to the table cell.</li>
7323      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7324      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7325      * <li>Row index</li>
7326      * <li>Column index</li>
7327      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7328      */
7329     setRenderer : function(col, fn){
7330         this.config[col].renderer = fn;
7331     },
7332
7333     /**
7334      * Returns the width for the specified column.
7335      * @param {Number} col The column index
7336      * @return {Number}
7337      */
7338     getColumnWidth : function(col){
7339         return this.config[col].width * 1 || this.defaultWidth;
7340     },
7341
7342     /**
7343      * Sets the width for a column.
7344      * @param {Number} col The column index
7345      * @param {Number} width The new width
7346      */
7347     setColumnWidth : function(col, width, suppressEvent){
7348         this.config[col].width = width;
7349         this.totalWidth = null;
7350         if(!suppressEvent){
7351              this.fireEvent("widthchange", this, col, width);
7352         }
7353     },
7354
7355     /**
7356      * Returns the total width of all columns.
7357      * @param {Boolean} includeHidden True to include hidden column widths
7358      * @return {Number}
7359      */
7360     getTotalWidth : function(includeHidden){
7361         if(!this.totalWidth){
7362             this.totalWidth = 0;
7363             for(var i = 0, len = this.config.length; i < len; i++){
7364                 if(includeHidden || !this.isHidden(i)){
7365                     this.totalWidth += this.getColumnWidth(i);
7366                 }
7367             }
7368         }
7369         return this.totalWidth;
7370     },
7371
7372     /**
7373      * Returns the header for the specified column.
7374      * @param {Number} col The column index
7375      * @return {String}
7376      */
7377     getColumnHeader : function(col){
7378         return this.config[col].header;
7379     },
7380
7381     /**
7382      * Sets the header for a column.
7383      * @param {Number} col The column index
7384      * @param {String} header The new header
7385      */
7386     setColumnHeader : function(col, header){
7387         this.config[col].header = header;
7388         this.fireEvent("headerchange", this, col, header);
7389     },
7390
7391     /**
7392      * Returns the tooltip for the specified column.
7393      * @param {Number} col The column index
7394      * @return {String}
7395      */
7396     getColumnTooltip : function(col){
7397             return this.config[col].tooltip;
7398     },
7399     /**
7400      * Sets the tooltip for a column.
7401      * @param {Number} col The column index
7402      * @param {String} tooltip The new tooltip
7403      */
7404     setColumnTooltip : function(col, tooltip){
7405             this.config[col].tooltip = tooltip;
7406     },
7407
7408     /**
7409      * Returns the dataIndex for the specified column.
7410      * @param {Number} col The column index
7411      * @return {Number}
7412      */
7413     getDataIndex : function(col){
7414         return this.config[col].dataIndex;
7415     },
7416
7417     /**
7418      * Sets the dataIndex for a column.
7419      * @param {Number} col The column index
7420      * @param {Number} dataIndex The new dataIndex
7421      */
7422     setDataIndex : function(col, dataIndex){
7423         this.config[col].dataIndex = dataIndex;
7424     },
7425
7426     
7427     
7428     /**
7429      * Returns true if the cell is editable.
7430      * @param {Number} colIndex The column index
7431      * @param {Number} rowIndex The row index - this is nto actually used..?
7432      * @return {Boolean}
7433      */
7434     isCellEditable : function(colIndex, rowIndex){
7435         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7436     },
7437
7438     /**
7439      * Returns the editor defined for the cell/column.
7440      * return false or null to disable editing.
7441      * @param {Number} colIndex The column index
7442      * @param {Number} rowIndex The row index
7443      * @return {Object}
7444      */
7445     getCellEditor : function(colIndex, rowIndex){
7446         return this.config[colIndex].editor;
7447     },
7448
7449     /**
7450      * Sets if a column is editable.
7451      * @param {Number} col The column index
7452      * @param {Boolean} editable True if the column is editable
7453      */
7454     setEditable : function(col, editable){
7455         this.config[col].editable = editable;
7456     },
7457
7458
7459     /**
7460      * Returns true if the column is hidden.
7461      * @param {Number} colIndex The column index
7462      * @return {Boolean}
7463      */
7464     isHidden : function(colIndex){
7465         return this.config[colIndex].hidden;
7466     },
7467
7468
7469     /**
7470      * Returns true if the column width cannot be changed
7471      */
7472     isFixed : function(colIndex){
7473         return this.config[colIndex].fixed;
7474     },
7475
7476     /**
7477      * Returns true if the column can be resized
7478      * @return {Boolean}
7479      */
7480     isResizable : function(colIndex){
7481         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7482     },
7483     /**
7484      * Sets if a column is hidden.
7485      * @param {Number} colIndex The column index
7486      * @param {Boolean} hidden True if the column is hidden
7487      */
7488     setHidden : function(colIndex, hidden){
7489         this.config[colIndex].hidden = hidden;
7490         this.totalWidth = null;
7491         this.fireEvent("hiddenchange", this, colIndex, hidden);
7492     },
7493
7494     /**
7495      * Sets the editor for a column.
7496      * @param {Number} col The column index
7497      * @param {Object} editor The editor object
7498      */
7499     setEditor : function(col, editor){
7500         this.config[col].editor = editor;
7501     }
7502 });
7503
7504 Roo.grid.ColumnModel.defaultRenderer = function(value)
7505 {
7506     if(typeof value == "object") {
7507         return value;
7508     }
7509         if(typeof value == "string" && value.length < 1){
7510             return "&#160;";
7511         }
7512     
7513         return String.format("{0}", value);
7514 };
7515
7516 // Alias for backwards compatibility
7517 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7518 /*
7519  * Based on:
7520  * Ext JS Library 1.1.1
7521  * Copyright(c) 2006-2007, Ext JS, LLC.
7522  *
7523  * Originally Released Under LGPL - original licence link has changed is not relivant.
7524  *
7525  * Fork - LGPL
7526  * <script type="text/javascript">
7527  */
7528  
7529 /**
7530  * @class Roo.LoadMask
7531  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7532  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7533  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7534  * element's UpdateManager load indicator and will be destroyed after the initial load.
7535  * @constructor
7536  * Create a new LoadMask
7537  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7538  * @param {Object} config The config object
7539  */
7540 Roo.LoadMask = function(el, config){
7541     this.el = Roo.get(el);
7542     Roo.apply(this, config);
7543     if(this.store){
7544         this.store.on('beforeload', this.onBeforeLoad, this);
7545         this.store.on('load', this.onLoad, this);
7546         this.store.on('loadexception', this.onLoadException, this);
7547         this.removeMask = false;
7548     }else{
7549         var um = this.el.getUpdateManager();
7550         um.showLoadIndicator = false; // disable the default indicator
7551         um.on('beforeupdate', this.onBeforeLoad, this);
7552         um.on('update', this.onLoad, this);
7553         um.on('failure', this.onLoad, this);
7554         this.removeMask = true;
7555     }
7556 };
7557
7558 Roo.LoadMask.prototype = {
7559     /**
7560      * @cfg {Boolean} removeMask
7561      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7562      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7563      */
7564     /**
7565      * @cfg {String} msg
7566      * The text to display in a centered loading message box (defaults to 'Loading...')
7567      */
7568     msg : 'Loading...',
7569     /**
7570      * @cfg {String} msgCls
7571      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7572      */
7573     msgCls : 'x-mask-loading',
7574
7575     /**
7576      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7577      * @type Boolean
7578      */
7579     disabled: false,
7580
7581     /**
7582      * Disables the mask to prevent it from being displayed
7583      */
7584     disable : function(){
7585        this.disabled = true;
7586     },
7587
7588     /**
7589      * Enables the mask so that it can be displayed
7590      */
7591     enable : function(){
7592         this.disabled = false;
7593     },
7594     
7595     onLoadException : function()
7596     {
7597         Roo.log(arguments);
7598         
7599         if (typeof(arguments[3]) != 'undefined') {
7600             Roo.MessageBox.alert("Error loading",arguments[3]);
7601         } 
7602         /*
7603         try {
7604             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7605                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7606             }   
7607         } catch(e) {
7608             
7609         }
7610         */
7611     
7612         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7613     },
7614     // private
7615     onLoad : function()
7616     {
7617         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7618     },
7619
7620     // private
7621     onBeforeLoad : function(){
7622         if(!this.disabled){
7623             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7624         }
7625     },
7626
7627     // private
7628     destroy : function(){
7629         if(this.store){
7630             this.store.un('beforeload', this.onBeforeLoad, this);
7631             this.store.un('load', this.onLoad, this);
7632             this.store.un('loadexception', this.onLoadException, this);
7633         }else{
7634             var um = this.el.getUpdateManager();
7635             um.un('beforeupdate', this.onBeforeLoad, this);
7636             um.un('update', this.onLoad, this);
7637             um.un('failure', this.onLoad, this);
7638         }
7639     }
7640 };/*
7641  * - LGPL
7642  *
7643  * table
7644  * 
7645  */
7646
7647 /**
7648  * @class Roo.bootstrap.Table
7649  * @extends Roo.bootstrap.Component
7650  * Bootstrap Table class
7651  * @cfg {String} cls table class
7652  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7653  * @cfg {String} bgcolor Specifies the background color for a table
7654  * @cfg {Number} border Specifies whether the table cells should have borders or not
7655  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7656  * @cfg {Number} cellspacing Specifies the space between cells
7657  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7658  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7659  * @cfg {String} sortable Specifies that the table should be sortable
7660  * @cfg {String} summary Specifies a summary of the content of a table
7661  * @cfg {Number} width Specifies the width of a table
7662  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7663  * 
7664  * @cfg {boolean} striped Should the rows be alternative striped
7665  * @cfg {boolean} bordered Add borders to the table
7666  * @cfg {boolean} hover Add hover highlighting
7667  * @cfg {boolean} condensed Format condensed
7668  * @cfg {boolean} responsive Format condensed
7669  * @cfg {Boolean} loadMask (true|false) default false
7670  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7671  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7672  * @cfg {Boolean} rowSelection (true|false) default false
7673  * @cfg {Boolean} cellSelection (true|false) default false
7674  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7675  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7676  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7677  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7678  
7679  * 
7680  * @constructor
7681  * Create a new Table
7682  * @param {Object} config The config object
7683  */
7684
7685 Roo.bootstrap.Table = function(config){
7686     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7687     
7688   
7689     
7690     // BC...
7691     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7692     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7693     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7694     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7695     
7696     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7697     if (this.sm) {
7698         this.sm.grid = this;
7699         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7700         this.sm = this.selModel;
7701         this.sm.xmodule = this.xmodule || false;
7702     }
7703     
7704     if (this.cm && typeof(this.cm.config) == 'undefined') {
7705         this.colModel = new Roo.grid.ColumnModel(this.cm);
7706         this.cm = this.colModel;
7707         this.cm.xmodule = this.xmodule || false;
7708     }
7709     if (this.store) {
7710         this.store= Roo.factory(this.store, Roo.data);
7711         this.ds = this.store;
7712         this.ds.xmodule = this.xmodule || false;
7713          
7714     }
7715     if (this.footer && this.store) {
7716         this.footer.dataSource = this.ds;
7717         this.footer = Roo.factory(this.footer);
7718     }
7719     
7720     /** @private */
7721     this.addEvents({
7722         /**
7723          * @event cellclick
7724          * Fires when a cell is clicked
7725          * @param {Roo.bootstrap.Table} this
7726          * @param {Roo.Element} el
7727          * @param {Number} rowIndex
7728          * @param {Number} columnIndex
7729          * @param {Roo.EventObject} e
7730          */
7731         "cellclick" : true,
7732         /**
7733          * @event celldblclick
7734          * Fires when a cell is double clicked
7735          * @param {Roo.bootstrap.Table} this
7736          * @param {Roo.Element} el
7737          * @param {Number} rowIndex
7738          * @param {Number} columnIndex
7739          * @param {Roo.EventObject} e
7740          */
7741         "celldblclick" : true,
7742         /**
7743          * @event rowclick
7744          * Fires when a row is clicked
7745          * @param {Roo.bootstrap.Table} this
7746          * @param {Roo.Element} el
7747          * @param {Number} rowIndex
7748          * @param {Roo.EventObject} e
7749          */
7750         "rowclick" : true,
7751         /**
7752          * @event rowdblclick
7753          * Fires when a row is double clicked
7754          * @param {Roo.bootstrap.Table} this
7755          * @param {Roo.Element} el
7756          * @param {Number} rowIndex
7757          * @param {Roo.EventObject} e
7758          */
7759         "rowdblclick" : true,
7760         /**
7761          * @event mouseover
7762          * Fires when a mouseover occur
7763          * @param {Roo.bootstrap.Table} this
7764          * @param {Roo.Element} el
7765          * @param {Number} rowIndex
7766          * @param {Number} columnIndex
7767          * @param {Roo.EventObject} e
7768          */
7769         "mouseover" : true,
7770         /**
7771          * @event mouseout
7772          * Fires when a mouseout occur
7773          * @param {Roo.bootstrap.Table} this
7774          * @param {Roo.Element} el
7775          * @param {Number} rowIndex
7776          * @param {Number} columnIndex
7777          * @param {Roo.EventObject} e
7778          */
7779         "mouseout" : true,
7780         /**
7781          * @event rowclass
7782          * Fires when a row is rendered, so you can change add a style to it.
7783          * @param {Roo.bootstrap.Table} this
7784          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
7785          */
7786         'rowclass' : true,
7787           /**
7788          * @event rowsrendered
7789          * Fires when all the  rows have been rendered
7790          * @param {Roo.bootstrap.Table} this
7791          */
7792         'rowsrendered' : true,
7793         /**
7794          * @event contextmenu
7795          * The raw contextmenu event for the entire grid.
7796          * @param {Roo.EventObject} e
7797          */
7798         "contextmenu" : true,
7799         /**
7800          * @event rowcontextmenu
7801          * Fires when a row is right clicked
7802          * @param {Roo.bootstrap.Table} this
7803          * @param {Number} rowIndex
7804          * @param {Roo.EventObject} e
7805          */
7806         "rowcontextmenu" : true,
7807         /**
7808          * @event cellcontextmenu
7809          * Fires when a cell is right clicked
7810          * @param {Roo.bootstrap.Table} this
7811          * @param {Number} rowIndex
7812          * @param {Number} cellIndex
7813          * @param {Roo.EventObject} e
7814          */
7815          "cellcontextmenu" : true,
7816          /**
7817          * @event headercontextmenu
7818          * Fires when a header is right clicked
7819          * @param {Roo.bootstrap.Table} this
7820          * @param {Number} columnIndex
7821          * @param {Roo.EventObject} e
7822          */
7823         "headercontextmenu" : true
7824     });
7825 };
7826
7827 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
7828     
7829     cls: false,
7830     align: false,
7831     bgcolor: false,
7832     border: false,
7833     cellpadding: false,
7834     cellspacing: false,
7835     frame: false,
7836     rules: false,
7837     sortable: false,
7838     summary: false,
7839     width: false,
7840     striped : false,
7841     scrollBody : false,
7842     bordered: false,
7843     hover:  false,
7844     condensed : false,
7845     responsive : false,
7846     sm : false,
7847     cm : false,
7848     store : false,
7849     loadMask : false,
7850     footerShow : true,
7851     headerShow : true,
7852   
7853     rowSelection : false,
7854     cellSelection : false,
7855     layout : false,
7856     
7857     // Roo.Element - the tbody
7858     mainBody: false,
7859     // Roo.Element - thead element
7860     mainHead: false,
7861     
7862     container: false, // used by gridpanel...
7863     
7864     lazyLoad : false,
7865     
7866     CSS : Roo.util.CSS,
7867     
7868     auto_hide_footer : false,
7869     
7870     getAutoCreate : function()
7871     {
7872         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7873         
7874         cfg = {
7875             tag: 'table',
7876             cls : 'table',
7877             cn : []
7878         };
7879         if (this.scrollBody) {
7880             cfg.cls += ' table-body-fixed';
7881         }    
7882         if (this.striped) {
7883             cfg.cls += ' table-striped';
7884         }
7885         
7886         if (this.hover) {
7887             cfg.cls += ' table-hover';
7888         }
7889         if (this.bordered) {
7890             cfg.cls += ' table-bordered';
7891         }
7892         if (this.condensed) {
7893             cfg.cls += ' table-condensed';
7894         }
7895         if (this.responsive) {
7896             cfg.cls += ' table-responsive';
7897         }
7898         
7899         if (this.cls) {
7900             cfg.cls+=  ' ' +this.cls;
7901         }
7902         
7903         // this lot should be simplifed...
7904         var _t = this;
7905         var cp = [
7906             'align',
7907             'bgcolor',
7908             'border',
7909             'cellpadding',
7910             'cellspacing',
7911             'frame',
7912             'rules',
7913             'sortable',
7914             'summary',
7915             'width'
7916         ].forEach(function(k) {
7917             if (_t[k]) {
7918                 cfg[k] = _t[k];
7919             }
7920         });
7921         
7922         
7923         if (this.layout) {
7924             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7925         }
7926         
7927         if(this.store || this.cm){
7928             if(this.headerShow){
7929                 cfg.cn.push(this.renderHeader());
7930             }
7931             
7932             cfg.cn.push(this.renderBody());
7933             
7934             if(this.footerShow){
7935                 cfg.cn.push(this.renderFooter());
7936             }
7937             // where does this come from?
7938             //cfg.cls+=  ' TableGrid';
7939         }
7940         
7941         return { cn : [ cfg ] };
7942     },
7943     
7944     initEvents : function()
7945     {   
7946         if(!this.store || !this.cm){
7947             return;
7948         }
7949         if (this.selModel) {
7950             this.selModel.initEvents();
7951         }
7952         
7953         
7954         //Roo.log('initEvents with ds!!!!');
7955         
7956         this.mainBody = this.el.select('tbody', true).first();
7957         this.mainHead = this.el.select('thead', true).first();
7958         this.mainFoot = this.el.select('tfoot', true).first();
7959         
7960         
7961         
7962         var _this = this;
7963         
7964         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7965             e.on('click', _this.sort, _this);
7966         });
7967         
7968         this.mainBody.on("click", this.onClick, this);
7969         this.mainBody.on("dblclick", this.onDblClick, this);
7970         
7971         // why is this done????? = it breaks dialogs??
7972         //this.parent().el.setStyle('position', 'relative');
7973         
7974         
7975         if (this.footer) {
7976             this.footer.parentId = this.id;
7977             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7978             
7979             if(this.lazyLoad){
7980                 this.el.select('tfoot tr td').first().addClass('hide');
7981             }
7982         } 
7983         
7984         if(this.loadMask) {
7985             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7986         }
7987         
7988         this.store.on('load', this.onLoad, this);
7989         this.store.on('beforeload', this.onBeforeLoad, this);
7990         this.store.on('update', this.onUpdate, this);
7991         this.store.on('add', this.onAdd, this);
7992         this.store.on("clear", this.clear, this);
7993         
7994         this.el.on("contextmenu", this.onContextMenu, this);
7995         
7996         this.mainBody.on('scroll', this.onBodyScroll, this);
7997         
7998         this.cm.on("headerchange", this.onHeaderChange, this);
7999         
8000         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8001         
8002     },
8003     
8004     onContextMenu : function(e, t)
8005     {
8006         this.processEvent("contextmenu", e);
8007     },
8008     
8009     processEvent : function(name, e)
8010     {
8011         if (name != 'touchstart' ) {
8012             this.fireEvent(name, e);    
8013         }
8014         
8015         var t = e.getTarget();
8016         
8017         var cell = Roo.get(t);
8018         
8019         if(!cell){
8020             return;
8021         }
8022         
8023         if(cell.findParent('tfoot', false, true)){
8024             return;
8025         }
8026         
8027         if(cell.findParent('thead', false, true)){
8028             
8029             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8030                 cell = Roo.get(t).findParent('th', false, true);
8031                 if (!cell) {
8032                     Roo.log("failed to find th in thead?");
8033                     Roo.log(e.getTarget());
8034                     return;
8035                 }
8036             }
8037             
8038             var cellIndex = cell.dom.cellIndex;
8039             
8040             var ename = name == 'touchstart' ? 'click' : name;
8041             this.fireEvent("header" + ename, this, cellIndex, e);
8042             
8043             return;
8044         }
8045         
8046         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8047             cell = Roo.get(t).findParent('td', false, true);
8048             if (!cell) {
8049                 Roo.log("failed to find th in tbody?");
8050                 Roo.log(e.getTarget());
8051                 return;
8052             }
8053         }
8054         
8055         var row = cell.findParent('tr', false, true);
8056         var cellIndex = cell.dom.cellIndex;
8057         var rowIndex = row.dom.rowIndex - 1;
8058         
8059         if(row !== false){
8060             
8061             this.fireEvent("row" + name, this, rowIndex, e);
8062             
8063             if(cell !== false){
8064             
8065                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8066             }
8067         }
8068         
8069     },
8070     
8071     onMouseover : function(e, el)
8072     {
8073         var cell = Roo.get(el);
8074         
8075         if(!cell){
8076             return;
8077         }
8078         
8079         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8080             cell = cell.findParent('td', false, true);
8081         }
8082         
8083         var row = cell.findParent('tr', false, true);
8084         var cellIndex = cell.dom.cellIndex;
8085         var rowIndex = row.dom.rowIndex - 1; // start from 0
8086         
8087         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8088         
8089     },
8090     
8091     onMouseout : function(e, el)
8092     {
8093         var cell = Roo.get(el);
8094         
8095         if(!cell){
8096             return;
8097         }
8098         
8099         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8100             cell = cell.findParent('td', false, true);
8101         }
8102         
8103         var row = cell.findParent('tr', false, true);
8104         var cellIndex = cell.dom.cellIndex;
8105         var rowIndex = row.dom.rowIndex - 1; // start from 0
8106         
8107         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8108         
8109     },
8110     
8111     onClick : function(e, el)
8112     {
8113         var cell = Roo.get(el);
8114         
8115         if(!cell || (!this.cellSelection && !this.rowSelection)){
8116             return;
8117         }
8118         
8119         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8120             cell = cell.findParent('td', false, true);
8121         }
8122         
8123         if(!cell || typeof(cell) == 'undefined'){
8124             return;
8125         }
8126         
8127         var row = cell.findParent('tr', false, true);
8128         
8129         if(!row || typeof(row) == 'undefined'){
8130             return;
8131         }
8132         
8133         var cellIndex = cell.dom.cellIndex;
8134         var rowIndex = this.getRowIndex(row);
8135         
8136         // why??? - should these not be based on SelectionModel?
8137         if(this.cellSelection){
8138             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8139         }
8140         
8141         if(this.rowSelection){
8142             this.fireEvent('rowclick', this, row, rowIndex, e);
8143         }
8144         
8145         
8146     },
8147         
8148     onDblClick : function(e,el)
8149     {
8150         var cell = Roo.get(el);
8151         
8152         if(!cell || (!this.cellSelection && !this.rowSelection)){
8153             return;
8154         }
8155         
8156         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8157             cell = cell.findParent('td', false, true);
8158         }
8159         
8160         if(!cell || typeof(cell) == 'undefined'){
8161             return;
8162         }
8163         
8164         var row = cell.findParent('tr', false, true);
8165         
8166         if(!row || typeof(row) == 'undefined'){
8167             return;
8168         }
8169         
8170         var cellIndex = cell.dom.cellIndex;
8171         var rowIndex = this.getRowIndex(row);
8172         
8173         if(this.cellSelection){
8174             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8175         }
8176         
8177         if(this.rowSelection){
8178             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8179         }
8180     },
8181     
8182     sort : function(e,el)
8183     {
8184         var col = Roo.get(el);
8185         
8186         if(!col.hasClass('sortable')){
8187             return;
8188         }
8189         
8190         var sort = col.attr('sort');
8191         var dir = 'ASC';
8192         
8193         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8194             dir = 'DESC';
8195         }
8196         
8197         this.store.sortInfo = {field : sort, direction : dir};
8198         
8199         if (this.footer) {
8200             Roo.log("calling footer first");
8201             this.footer.onClick('first');
8202         } else {
8203         
8204             this.store.load({ params : { start : 0 } });
8205         }
8206     },
8207     
8208     renderHeader : function()
8209     {
8210         var header = {
8211             tag: 'thead',
8212             cn : []
8213         };
8214         
8215         var cm = this.cm;
8216         this.totalWidth = 0;
8217         
8218         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8219             
8220             var config = cm.config[i];
8221             
8222             var c = {
8223                 tag: 'th',
8224                 cls : 'x-hcol-' + i,
8225                 style : '',
8226                 html: cm.getColumnHeader(i)
8227             };
8228             
8229             var hh = '';
8230             
8231             if(typeof(config.sortable) != 'undefined' && config.sortable){
8232                 c.cls = 'sortable';
8233                 c.html = '<i class="glyphicon"></i>' + c.html;
8234             }
8235             
8236             // could use BS4 hidden-..-down 
8237             
8238             if(typeof(config.lgHeader) != 'undefined'){
8239                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8240             }
8241             
8242             if(typeof(config.mdHeader) != 'undefined'){
8243                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8244             }
8245             
8246             if(typeof(config.smHeader) != 'undefined'){
8247                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8248             }
8249             
8250             if(typeof(config.xsHeader) != 'undefined'){
8251                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8252             }
8253             
8254             if(hh.length){
8255                 c.html = hh;
8256             }
8257             
8258             if(typeof(config.tooltip) != 'undefined'){
8259                 c.tooltip = config.tooltip;
8260             }
8261             
8262             if(typeof(config.colspan) != 'undefined'){
8263                 c.colspan = config.colspan;
8264             }
8265             
8266             if(typeof(config.hidden) != 'undefined' && config.hidden){
8267                 c.style += ' display:none;';
8268             }
8269             
8270             if(typeof(config.dataIndex) != 'undefined'){
8271                 c.sort = config.dataIndex;
8272             }
8273             
8274            
8275             
8276             if(typeof(config.align) != 'undefined' && config.align.length){
8277                 c.style += ' text-align:' + config.align + ';';
8278             }
8279             
8280             if(typeof(config.width) != 'undefined'){
8281                 c.style += ' width:' + config.width + 'px;';
8282                 this.totalWidth += config.width;
8283             } else {
8284                 this.totalWidth += 100; // assume minimum of 100 per column?
8285             }
8286             
8287             if(typeof(config.cls) != 'undefined'){
8288                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8289             }
8290             
8291             ['xs','sm','md','lg'].map(function(size){
8292                 
8293                 if(typeof(config[size]) == 'undefined'){
8294                     return;
8295                 }
8296                  
8297                 if (!config[size]) { // 0 = hidden
8298                     // BS 4 '0' is treated as hide that column and below.
8299                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8300                     return;
8301                 }
8302                 
8303                 c.cls += ' col-' + size + '-' + config[size] + (
8304                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8305                 );
8306                 
8307                 
8308             });
8309             
8310             header.cn.push(c)
8311         }
8312         
8313         return header;
8314     },
8315     
8316     renderBody : function()
8317     {
8318         var body = {
8319             tag: 'tbody',
8320             cn : [
8321                 {
8322                     tag: 'tr',
8323                     cn : [
8324                         {
8325                             tag : 'td',
8326                             colspan :  this.cm.getColumnCount()
8327                         }
8328                     ]
8329                 }
8330             ]
8331         };
8332         
8333         return body;
8334     },
8335     
8336     renderFooter : function()
8337     {
8338         var footer = {
8339             tag: 'tfoot',
8340             cn : [
8341                 {
8342                     tag: 'tr',
8343                     cn : [
8344                         {
8345                             tag : 'td',
8346                             colspan :  this.cm.getColumnCount()
8347                         }
8348                     ]
8349                 }
8350             ]
8351         };
8352         
8353         return footer;
8354     },
8355     
8356     
8357     
8358     onLoad : function()
8359     {
8360 //        Roo.log('ds onload');
8361         this.clear();
8362         
8363         var _this = this;
8364         var cm = this.cm;
8365         var ds = this.store;
8366         
8367         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8368             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8369             if (_this.store.sortInfo) {
8370                     
8371                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8372                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8373                 }
8374                 
8375                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8376                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8377                 }
8378             }
8379         });
8380         
8381         var tbody =  this.mainBody;
8382               
8383         if(ds.getCount() > 0){
8384             ds.data.each(function(d,rowIndex){
8385                 var row =  this.renderRow(cm, ds, rowIndex);
8386                 
8387                 tbody.createChild(row);
8388                 
8389                 var _this = this;
8390                 
8391                 if(row.cellObjects.length){
8392                     Roo.each(row.cellObjects, function(r){
8393                         _this.renderCellObject(r);
8394                     })
8395                 }
8396                 
8397             }, this);
8398         }
8399         
8400         var tfoot = this.el.select('tfoot', true).first();
8401         
8402         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8403             
8404             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8405             
8406             var total = this.ds.getTotalCount();
8407             
8408             if(this.footer.pageSize < total){
8409                 this.mainFoot.show();
8410             }
8411         }
8412         
8413         Roo.each(this.el.select('tbody td', true).elements, function(e){
8414             e.on('mouseover', _this.onMouseover, _this);
8415         });
8416         
8417         Roo.each(this.el.select('tbody td', true).elements, function(e){
8418             e.on('mouseout', _this.onMouseout, _this);
8419         });
8420         this.fireEvent('rowsrendered', this);
8421         
8422         this.autoSize();
8423     },
8424     
8425     
8426     onUpdate : function(ds,record)
8427     {
8428         this.refreshRow(record);
8429         this.autoSize();
8430     },
8431     
8432     onRemove : function(ds, record, index, isUpdate){
8433         if(isUpdate !== true){
8434             this.fireEvent("beforerowremoved", this, index, record);
8435         }
8436         var bt = this.mainBody.dom;
8437         
8438         var rows = this.el.select('tbody > tr', true).elements;
8439         
8440         if(typeof(rows[index]) != 'undefined'){
8441             bt.removeChild(rows[index].dom);
8442         }
8443         
8444 //        if(bt.rows[index]){
8445 //            bt.removeChild(bt.rows[index]);
8446 //        }
8447         
8448         if(isUpdate !== true){
8449             //this.stripeRows(index);
8450             //this.syncRowHeights(index, index);
8451             //this.layout();
8452             this.fireEvent("rowremoved", this, index, record);
8453         }
8454     },
8455     
8456     onAdd : function(ds, records, rowIndex)
8457     {
8458         //Roo.log('on Add called');
8459         // - note this does not handle multiple adding very well..
8460         var bt = this.mainBody.dom;
8461         for (var i =0 ; i < records.length;i++) {
8462             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8463             //Roo.log(records[i]);
8464             //Roo.log(this.store.getAt(rowIndex+i));
8465             this.insertRow(this.store, rowIndex + i, false);
8466             return;
8467         }
8468         
8469     },
8470     
8471     
8472     refreshRow : function(record){
8473         var ds = this.store, index;
8474         if(typeof record == 'number'){
8475             index = record;
8476             record = ds.getAt(index);
8477         }else{
8478             index = ds.indexOf(record);
8479             if (index < 0) {
8480                 return; // should not happen - but seems to 
8481             }
8482         }
8483         this.insertRow(ds, index, true);
8484         this.autoSize();
8485         this.onRemove(ds, record, index+1, true);
8486         this.autoSize();
8487         //this.syncRowHeights(index, index);
8488         //this.layout();
8489         this.fireEvent("rowupdated", this, index, record);
8490     },
8491     
8492     insertRow : function(dm, rowIndex, isUpdate){
8493         
8494         if(!isUpdate){
8495             this.fireEvent("beforerowsinserted", this, rowIndex);
8496         }
8497             //var s = this.getScrollState();
8498         var row = this.renderRow(this.cm, this.store, rowIndex);
8499         // insert before rowIndex..
8500         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8501         
8502         var _this = this;
8503                 
8504         if(row.cellObjects.length){
8505             Roo.each(row.cellObjects, function(r){
8506                 _this.renderCellObject(r);
8507             })
8508         }
8509             
8510         if(!isUpdate){
8511             this.fireEvent("rowsinserted", this, rowIndex);
8512             //this.syncRowHeights(firstRow, lastRow);
8513             //this.stripeRows(firstRow);
8514             //this.layout();
8515         }
8516         
8517     },
8518     
8519     
8520     getRowDom : function(rowIndex)
8521     {
8522         var rows = this.el.select('tbody > tr', true).elements;
8523         
8524         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8525         
8526     },
8527     // returns the object tree for a tr..
8528   
8529     
8530     renderRow : function(cm, ds, rowIndex) 
8531     {
8532         var d = ds.getAt(rowIndex);
8533         
8534         var row = {
8535             tag : 'tr',
8536             cls : 'x-row-' + rowIndex,
8537             cn : []
8538         };
8539             
8540         var cellObjects = [];
8541         
8542         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8543             var config = cm.config[i];
8544             
8545             var renderer = cm.getRenderer(i);
8546             var value = '';
8547             var id = false;
8548             
8549             if(typeof(renderer) !== 'undefined'){
8550                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8551             }
8552             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8553             // and are rendered into the cells after the row is rendered - using the id for the element.
8554             
8555             if(typeof(value) === 'object'){
8556                 id = Roo.id();
8557                 cellObjects.push({
8558                     container : id,
8559                     cfg : value 
8560                 })
8561             }
8562             
8563             var rowcfg = {
8564                 record: d,
8565                 rowIndex : rowIndex,
8566                 colIndex : i,
8567                 rowClass : ''
8568             };
8569
8570             this.fireEvent('rowclass', this, rowcfg);
8571             
8572             var td = {
8573                 tag: 'td',
8574                 cls : rowcfg.rowClass + ' x-col-' + i,
8575                 style: '',
8576                 html: (typeof(value) === 'object') ? '' : value
8577             };
8578             
8579             if (id) {
8580                 td.id = id;
8581             }
8582             
8583             if(typeof(config.colspan) != 'undefined'){
8584                 td.colspan = config.colspan;
8585             }
8586             
8587             if(typeof(config.hidden) != 'undefined' && config.hidden){
8588                 td.style += ' display:none;';
8589             }
8590             
8591             if(typeof(config.align) != 'undefined' && config.align.length){
8592                 td.style += ' text-align:' + config.align + ';';
8593             }
8594             if(typeof(config.valign) != 'undefined' && config.valign.length){
8595                 td.style += ' vertical-align:' + config.valign + ';';
8596             }
8597             
8598             if(typeof(config.width) != 'undefined'){
8599                 td.style += ' width:' +  config.width + 'px;';
8600             }
8601             
8602             if(typeof(config.cursor) != 'undefined'){
8603                 td.style += ' cursor:' +  config.cursor + ';';
8604             }
8605             
8606             if(typeof(config.cls) != 'undefined'){
8607                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8608             }
8609             
8610             ['xs','sm','md','lg'].map(function(size){
8611                 
8612                 if(typeof(config[size]) == 'undefined'){
8613                     return;
8614                 }
8615                 
8616                 
8617                   
8618                 if (!config[size]) { // 0 = hidden
8619                     // BS 4 '0' is treated as hide that column and below.
8620                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8621                     return;
8622                 }
8623                 
8624                 td.cls += ' col-' + size + '-' + config[size] + (
8625                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8626                 );
8627                  
8628
8629             });
8630             
8631             row.cn.push(td);
8632            
8633         }
8634         
8635         row.cellObjects = cellObjects;
8636         
8637         return row;
8638           
8639     },
8640     
8641     
8642     
8643     onBeforeLoad : function()
8644     {
8645         
8646     },
8647      /**
8648      * Remove all rows
8649      */
8650     clear : function()
8651     {
8652         this.el.select('tbody', true).first().dom.innerHTML = '';
8653     },
8654     /**
8655      * Show or hide a row.
8656      * @param {Number} rowIndex to show or hide
8657      * @param {Boolean} state hide
8658      */
8659     setRowVisibility : function(rowIndex, state)
8660     {
8661         var bt = this.mainBody.dom;
8662         
8663         var rows = this.el.select('tbody > tr', true).elements;
8664         
8665         if(typeof(rows[rowIndex]) == 'undefined'){
8666             return;
8667         }
8668         rows[rowIndex].dom.style.display = state ? '' : 'none';
8669     },
8670     
8671     
8672     getSelectionModel : function(){
8673         if(!this.selModel){
8674             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8675         }
8676         return this.selModel;
8677     },
8678     /*
8679      * Render the Roo.bootstrap object from renderder
8680      */
8681     renderCellObject : function(r)
8682     {
8683         var _this = this;
8684         
8685         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8686         
8687         var t = r.cfg.render(r.container);
8688         
8689         if(r.cfg.cn){
8690             Roo.each(r.cfg.cn, function(c){
8691                 var child = {
8692                     container: t.getChildContainer(),
8693                     cfg: c
8694                 };
8695                 _this.renderCellObject(child);
8696             })
8697         }
8698     },
8699     
8700     getRowIndex : function(row)
8701     {
8702         var rowIndex = -1;
8703         
8704         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8705             if(el != row){
8706                 return;
8707             }
8708             
8709             rowIndex = index;
8710         });
8711         
8712         return rowIndex;
8713     },
8714      /**
8715      * Returns the grid's underlying element = used by panel.Grid
8716      * @return {Element} The element
8717      */
8718     getGridEl : function(){
8719         return this.el;
8720     },
8721      /**
8722      * Forces a resize - used by panel.Grid
8723      * @return {Element} The element
8724      */
8725     autoSize : function()
8726     {
8727         //var ctr = Roo.get(this.container.dom.parentElement);
8728         var ctr = Roo.get(this.el.dom);
8729         
8730         var thd = this.getGridEl().select('thead',true).first();
8731         var tbd = this.getGridEl().select('tbody', true).first();
8732         var tfd = this.getGridEl().select('tfoot', true).first();
8733         
8734         var cw = ctr.getWidth();
8735         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
8736         
8737         if (tbd) {
8738             
8739             tbd.setWidth(ctr.getWidth());
8740             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8741             // this needs fixing for various usage - currently only hydra job advers I think..
8742             //tdb.setHeight(
8743             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8744             //); 
8745             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8746             cw -= barsize;
8747         }
8748         cw = Math.max(cw, this.totalWidth);
8749         this.getGridEl().select('tbody tr',true).setWidth(cw);
8750         
8751         // resize 'expandable coloumn?
8752         
8753         return; // we doe not have a view in this design..
8754         
8755     },
8756     onBodyScroll: function()
8757     {
8758         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8759         if(this.mainHead){
8760             this.mainHead.setStyle({
8761                 'position' : 'relative',
8762                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8763             });
8764         }
8765         
8766         if(this.lazyLoad){
8767             
8768             var scrollHeight = this.mainBody.dom.scrollHeight;
8769             
8770             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8771             
8772             var height = this.mainBody.getHeight();
8773             
8774             if(scrollHeight - height == scrollTop) {
8775                 
8776                 var total = this.ds.getTotalCount();
8777                 
8778                 if(this.footer.cursor + this.footer.pageSize < total){
8779                     
8780                     this.footer.ds.load({
8781                         params : {
8782                             start : this.footer.cursor + this.footer.pageSize,
8783                             limit : this.footer.pageSize
8784                         },
8785                         add : true
8786                     });
8787                 }
8788             }
8789             
8790         }
8791     },
8792     
8793     onHeaderChange : function()
8794     {
8795         var header = this.renderHeader();
8796         var table = this.el.select('table', true).first();
8797         
8798         this.mainHead.remove();
8799         this.mainHead = table.createChild(header, this.mainBody, false);
8800     },
8801     
8802     onHiddenChange : function(colModel, colIndex, hidden)
8803     {
8804         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8805         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8806         
8807         this.CSS.updateRule(thSelector, "display", "");
8808         this.CSS.updateRule(tdSelector, "display", "");
8809         
8810         if(hidden){
8811             this.CSS.updateRule(thSelector, "display", "none");
8812             this.CSS.updateRule(tdSelector, "display", "none");
8813         }
8814         
8815         this.onHeaderChange();
8816         this.onLoad();
8817     },
8818     
8819     setColumnWidth: function(col_index, width)
8820     {
8821         // width = "md-2 xs-2..."
8822         if(!this.colModel.config[col_index]) {
8823             return;
8824         }
8825         
8826         var w = width.split(" ");
8827         
8828         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8829         
8830         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8831         
8832         
8833         for(var j = 0; j < w.length; j++) {
8834             
8835             if(!w[j]) {
8836                 continue;
8837             }
8838             
8839             var size_cls = w[j].split("-");
8840             
8841             if(!Number.isInteger(size_cls[1] * 1)) {
8842                 continue;
8843             }
8844             
8845             if(!this.colModel.config[col_index][size_cls[0]]) {
8846                 continue;
8847             }
8848             
8849             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8850                 continue;
8851             }
8852             
8853             h_row[0].classList.replace(
8854                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8855                 "col-"+size_cls[0]+"-"+size_cls[1]
8856             );
8857             
8858             for(var i = 0; i < rows.length; i++) {
8859                 
8860                 var size_cls = w[j].split("-");
8861                 
8862                 if(!Number.isInteger(size_cls[1] * 1)) {
8863                     continue;
8864                 }
8865                 
8866                 if(!this.colModel.config[col_index][size_cls[0]]) {
8867                     continue;
8868                 }
8869                 
8870                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8871                     continue;
8872                 }
8873                 
8874                 rows[i].classList.replace(
8875                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8876                     "col-"+size_cls[0]+"-"+size_cls[1]
8877                 );
8878             }
8879             
8880             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8881         }
8882     }
8883 });
8884
8885  
8886
8887  /*
8888  * - LGPL
8889  *
8890  * table cell
8891  * 
8892  */
8893
8894 /**
8895  * @class Roo.bootstrap.TableCell
8896  * @extends Roo.bootstrap.Component
8897  * Bootstrap TableCell class
8898  * @cfg {String} html cell contain text
8899  * @cfg {String} cls cell class
8900  * @cfg {String} tag cell tag (td|th) default td
8901  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8902  * @cfg {String} align Aligns the content in a cell
8903  * @cfg {String} axis Categorizes cells
8904  * @cfg {String} bgcolor Specifies the background color of a cell
8905  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8906  * @cfg {Number} colspan Specifies the number of columns a cell should span
8907  * @cfg {String} headers Specifies one or more header cells a cell is related to
8908  * @cfg {Number} height Sets the height of a cell
8909  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8910  * @cfg {Number} rowspan Sets the number of rows a cell should span
8911  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8912  * @cfg {String} valign Vertical aligns the content in a cell
8913  * @cfg {Number} width Specifies the width of a cell
8914  * 
8915  * @constructor
8916  * Create a new TableCell
8917  * @param {Object} config The config object
8918  */
8919
8920 Roo.bootstrap.TableCell = function(config){
8921     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8922 };
8923
8924 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
8925     
8926     html: false,
8927     cls: false,
8928     tag: false,
8929     abbr: false,
8930     align: false,
8931     axis: false,
8932     bgcolor: false,
8933     charoff: false,
8934     colspan: false,
8935     headers: false,
8936     height: false,
8937     nowrap: false,
8938     rowspan: false,
8939     scope: false,
8940     valign: false,
8941     width: false,
8942     
8943     
8944     getAutoCreate : function(){
8945         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8946         
8947         cfg = {
8948             tag: 'td'
8949         };
8950         
8951         if(this.tag){
8952             cfg.tag = this.tag;
8953         }
8954         
8955         if (this.html) {
8956             cfg.html=this.html
8957         }
8958         if (this.cls) {
8959             cfg.cls=this.cls
8960         }
8961         if (this.abbr) {
8962             cfg.abbr=this.abbr
8963         }
8964         if (this.align) {
8965             cfg.align=this.align
8966         }
8967         if (this.axis) {
8968             cfg.axis=this.axis
8969         }
8970         if (this.bgcolor) {
8971             cfg.bgcolor=this.bgcolor
8972         }
8973         if (this.charoff) {
8974             cfg.charoff=this.charoff
8975         }
8976         if (this.colspan) {
8977             cfg.colspan=this.colspan
8978         }
8979         if (this.headers) {
8980             cfg.headers=this.headers
8981         }
8982         if (this.height) {
8983             cfg.height=this.height
8984         }
8985         if (this.nowrap) {
8986             cfg.nowrap=this.nowrap
8987         }
8988         if (this.rowspan) {
8989             cfg.rowspan=this.rowspan
8990         }
8991         if (this.scope) {
8992             cfg.scope=this.scope
8993         }
8994         if (this.valign) {
8995             cfg.valign=this.valign
8996         }
8997         if (this.width) {
8998             cfg.width=this.width
8999         }
9000         
9001         
9002         return cfg;
9003     }
9004    
9005 });
9006
9007  
9008
9009  /*
9010  * - LGPL
9011  *
9012  * table row
9013  * 
9014  */
9015
9016 /**
9017  * @class Roo.bootstrap.TableRow
9018  * @extends Roo.bootstrap.Component
9019  * Bootstrap TableRow class
9020  * @cfg {String} cls row class
9021  * @cfg {String} align Aligns the content in a table row
9022  * @cfg {String} bgcolor Specifies a background color for a table row
9023  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9024  * @cfg {String} valign Vertical aligns the content in a table row
9025  * 
9026  * @constructor
9027  * Create a new TableRow
9028  * @param {Object} config The config object
9029  */
9030
9031 Roo.bootstrap.TableRow = function(config){
9032     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9033 };
9034
9035 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9036     
9037     cls: false,
9038     align: false,
9039     bgcolor: false,
9040     charoff: false,
9041     valign: false,
9042     
9043     getAutoCreate : function(){
9044         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9045         
9046         cfg = {
9047             tag: 'tr'
9048         };
9049             
9050         if(this.cls){
9051             cfg.cls = this.cls;
9052         }
9053         if(this.align){
9054             cfg.align = this.align;
9055         }
9056         if(this.bgcolor){
9057             cfg.bgcolor = this.bgcolor;
9058         }
9059         if(this.charoff){
9060             cfg.charoff = this.charoff;
9061         }
9062         if(this.valign){
9063             cfg.valign = this.valign;
9064         }
9065         
9066         return cfg;
9067     }
9068    
9069 });
9070
9071  
9072
9073  /*
9074  * - LGPL
9075  *
9076  * table body
9077  * 
9078  */
9079
9080 /**
9081  * @class Roo.bootstrap.TableBody
9082  * @extends Roo.bootstrap.Component
9083  * Bootstrap TableBody class
9084  * @cfg {String} cls element class
9085  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9086  * @cfg {String} align Aligns the content inside the element
9087  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9088  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9089  * 
9090  * @constructor
9091  * Create a new TableBody
9092  * @param {Object} config The config object
9093  */
9094
9095 Roo.bootstrap.TableBody = function(config){
9096     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9097 };
9098
9099 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9100     
9101     cls: false,
9102     tag: false,
9103     align: false,
9104     charoff: false,
9105     valign: false,
9106     
9107     getAutoCreate : function(){
9108         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9109         
9110         cfg = {
9111             tag: 'tbody'
9112         };
9113             
9114         if (this.cls) {
9115             cfg.cls=this.cls
9116         }
9117         if(this.tag){
9118             cfg.tag = this.tag;
9119         }
9120         
9121         if(this.align){
9122             cfg.align = this.align;
9123         }
9124         if(this.charoff){
9125             cfg.charoff = this.charoff;
9126         }
9127         if(this.valign){
9128             cfg.valign = this.valign;
9129         }
9130         
9131         return cfg;
9132     }
9133     
9134     
9135 //    initEvents : function()
9136 //    {
9137 //        
9138 //        if(!this.store){
9139 //            return;
9140 //        }
9141 //        
9142 //        this.store = Roo.factory(this.store, Roo.data);
9143 //        this.store.on('load', this.onLoad, this);
9144 //        
9145 //        this.store.load();
9146 //        
9147 //    },
9148 //    
9149 //    onLoad: function () 
9150 //    {   
9151 //        this.fireEvent('load', this);
9152 //    }
9153 //    
9154 //   
9155 });
9156
9157  
9158
9159  /*
9160  * Based on:
9161  * Ext JS Library 1.1.1
9162  * Copyright(c) 2006-2007, Ext JS, LLC.
9163  *
9164  * Originally Released Under LGPL - original licence link has changed is not relivant.
9165  *
9166  * Fork - LGPL
9167  * <script type="text/javascript">
9168  */
9169
9170 // as we use this in bootstrap.
9171 Roo.namespace('Roo.form');
9172  /**
9173  * @class Roo.form.Action
9174  * Internal Class used to handle form actions
9175  * @constructor
9176  * @param {Roo.form.BasicForm} el The form element or its id
9177  * @param {Object} config Configuration options
9178  */
9179
9180  
9181  
9182 // define the action interface
9183 Roo.form.Action = function(form, options){
9184     this.form = form;
9185     this.options = options || {};
9186 };
9187 /**
9188  * Client Validation Failed
9189  * @const 
9190  */
9191 Roo.form.Action.CLIENT_INVALID = 'client';
9192 /**
9193  * Server Validation Failed
9194  * @const 
9195  */
9196 Roo.form.Action.SERVER_INVALID = 'server';
9197  /**
9198  * Connect to Server Failed
9199  * @const 
9200  */
9201 Roo.form.Action.CONNECT_FAILURE = 'connect';
9202 /**
9203  * Reading Data from Server Failed
9204  * @const 
9205  */
9206 Roo.form.Action.LOAD_FAILURE = 'load';
9207
9208 Roo.form.Action.prototype = {
9209     type : 'default',
9210     failureType : undefined,
9211     response : undefined,
9212     result : undefined,
9213
9214     // interface method
9215     run : function(options){
9216
9217     },
9218
9219     // interface method
9220     success : function(response){
9221
9222     },
9223
9224     // interface method
9225     handleResponse : function(response){
9226
9227     },
9228
9229     // default connection failure
9230     failure : function(response){
9231         
9232         this.response = response;
9233         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9234         this.form.afterAction(this, false);
9235     },
9236
9237     processResponse : function(response){
9238         this.response = response;
9239         if(!response.responseText){
9240             return true;
9241         }
9242         this.result = this.handleResponse(response);
9243         return this.result;
9244     },
9245
9246     // utility functions used internally
9247     getUrl : function(appendParams){
9248         var url = this.options.url || this.form.url || this.form.el.dom.action;
9249         if(appendParams){
9250             var p = this.getParams();
9251             if(p){
9252                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9253             }
9254         }
9255         return url;
9256     },
9257
9258     getMethod : function(){
9259         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9260     },
9261
9262     getParams : function(){
9263         var bp = this.form.baseParams;
9264         var p = this.options.params;
9265         if(p){
9266             if(typeof p == "object"){
9267                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9268             }else if(typeof p == 'string' && bp){
9269                 p += '&' + Roo.urlEncode(bp);
9270             }
9271         }else if(bp){
9272             p = Roo.urlEncode(bp);
9273         }
9274         return p;
9275     },
9276
9277     createCallback : function(){
9278         return {
9279             success: this.success,
9280             failure: this.failure,
9281             scope: this,
9282             timeout: (this.form.timeout*1000),
9283             upload: this.form.fileUpload ? this.success : undefined
9284         };
9285     }
9286 };
9287
9288 Roo.form.Action.Submit = function(form, options){
9289     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9290 };
9291
9292 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9293     type : 'submit',
9294
9295     haveProgress : false,
9296     uploadComplete : false,
9297     
9298     // uploadProgress indicator.
9299     uploadProgress : function()
9300     {
9301         if (!this.form.progressUrl) {
9302             return;
9303         }
9304         
9305         if (!this.haveProgress) {
9306             Roo.MessageBox.progress("Uploading", "Uploading");
9307         }
9308         if (this.uploadComplete) {
9309            Roo.MessageBox.hide();
9310            return;
9311         }
9312         
9313         this.haveProgress = true;
9314    
9315         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9316         
9317         var c = new Roo.data.Connection();
9318         c.request({
9319             url : this.form.progressUrl,
9320             params: {
9321                 id : uid
9322             },
9323             method: 'GET',
9324             success : function(req){
9325                //console.log(data);
9326                 var rdata = false;
9327                 var edata;
9328                 try  {
9329                    rdata = Roo.decode(req.responseText)
9330                 } catch (e) {
9331                     Roo.log("Invalid data from server..");
9332                     Roo.log(edata);
9333                     return;
9334                 }
9335                 if (!rdata || !rdata.success) {
9336                     Roo.log(rdata);
9337                     Roo.MessageBox.alert(Roo.encode(rdata));
9338                     return;
9339                 }
9340                 var data = rdata.data;
9341                 
9342                 if (this.uploadComplete) {
9343                    Roo.MessageBox.hide();
9344                    return;
9345                 }
9346                    
9347                 if (data){
9348                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9349                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9350                     );
9351                 }
9352                 this.uploadProgress.defer(2000,this);
9353             },
9354        
9355             failure: function(data) {
9356                 Roo.log('progress url failed ');
9357                 Roo.log(data);
9358             },
9359             scope : this
9360         });
9361            
9362     },
9363     
9364     
9365     run : function()
9366     {
9367         // run get Values on the form, so it syncs any secondary forms.
9368         this.form.getValues();
9369         
9370         var o = this.options;
9371         var method = this.getMethod();
9372         var isPost = method == 'POST';
9373         if(o.clientValidation === false || this.form.isValid()){
9374             
9375             if (this.form.progressUrl) {
9376                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9377                     (new Date() * 1) + '' + Math.random());
9378                     
9379             } 
9380             
9381             
9382             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9383                 form:this.form.el.dom,
9384                 url:this.getUrl(!isPost),
9385                 method: method,
9386                 params:isPost ? this.getParams() : null,
9387                 isUpload: this.form.fileUpload,
9388                 formData : this.form.formData
9389             }));
9390             
9391             this.uploadProgress();
9392
9393         }else if (o.clientValidation !== false){ // client validation failed
9394             this.failureType = Roo.form.Action.CLIENT_INVALID;
9395             this.form.afterAction(this, false);
9396         }
9397     },
9398
9399     success : function(response)
9400     {
9401         this.uploadComplete= true;
9402         if (this.haveProgress) {
9403             Roo.MessageBox.hide();
9404         }
9405         
9406         
9407         var result = this.processResponse(response);
9408         if(result === true || result.success){
9409             this.form.afterAction(this, true);
9410             return;
9411         }
9412         if(result.errors){
9413             this.form.markInvalid(result.errors);
9414             this.failureType = Roo.form.Action.SERVER_INVALID;
9415         }
9416         this.form.afterAction(this, false);
9417     },
9418     failure : function(response)
9419     {
9420         this.uploadComplete= true;
9421         if (this.haveProgress) {
9422             Roo.MessageBox.hide();
9423         }
9424         
9425         this.response = response;
9426         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9427         this.form.afterAction(this, false);
9428     },
9429     
9430     handleResponse : function(response){
9431         if(this.form.errorReader){
9432             var rs = this.form.errorReader.read(response);
9433             var errors = [];
9434             if(rs.records){
9435                 for(var i = 0, len = rs.records.length; i < len; i++) {
9436                     var r = rs.records[i];
9437                     errors[i] = r.data;
9438                 }
9439             }
9440             if(errors.length < 1){
9441                 errors = null;
9442             }
9443             return {
9444                 success : rs.success,
9445                 errors : errors
9446             };
9447         }
9448         var ret = false;
9449         try {
9450             ret = Roo.decode(response.responseText);
9451         } catch (e) {
9452             ret = {
9453                 success: false,
9454                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9455                 errors : []
9456             };
9457         }
9458         return ret;
9459         
9460     }
9461 });
9462
9463
9464 Roo.form.Action.Load = function(form, options){
9465     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9466     this.reader = this.form.reader;
9467 };
9468
9469 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9470     type : 'load',
9471
9472     run : function(){
9473         
9474         Roo.Ajax.request(Roo.apply(
9475                 this.createCallback(), {
9476                     method:this.getMethod(),
9477                     url:this.getUrl(false),
9478                     params:this.getParams()
9479         }));
9480     },
9481
9482     success : function(response){
9483         
9484         var result = this.processResponse(response);
9485         if(result === true || !result.success || !result.data){
9486             this.failureType = Roo.form.Action.LOAD_FAILURE;
9487             this.form.afterAction(this, false);
9488             return;
9489         }
9490         this.form.clearInvalid();
9491         this.form.setValues(result.data);
9492         this.form.afterAction(this, true);
9493     },
9494
9495     handleResponse : function(response){
9496         if(this.form.reader){
9497             var rs = this.form.reader.read(response);
9498             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9499             return {
9500                 success : rs.success,
9501                 data : data
9502             };
9503         }
9504         return Roo.decode(response.responseText);
9505     }
9506 });
9507
9508 Roo.form.Action.ACTION_TYPES = {
9509     'load' : Roo.form.Action.Load,
9510     'submit' : Roo.form.Action.Submit
9511 };/*
9512  * - LGPL
9513  *
9514  * form
9515  *
9516  */
9517
9518 /**
9519  * @class Roo.bootstrap.Form
9520  * @extends Roo.bootstrap.Component
9521  * Bootstrap Form class
9522  * @cfg {String} method  GET | POST (default POST)
9523  * @cfg {String} labelAlign top | left (default top)
9524  * @cfg {String} align left  | right - for navbars
9525  * @cfg {Boolean} loadMask load mask when submit (default true)
9526
9527  *
9528  * @constructor
9529  * Create a new Form
9530  * @param {Object} config The config object
9531  */
9532
9533
9534 Roo.bootstrap.Form = function(config){
9535     
9536     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9537     
9538     Roo.bootstrap.Form.popover.apply();
9539     
9540     this.addEvents({
9541         /**
9542          * @event clientvalidation
9543          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9544          * @param {Form} this
9545          * @param {Boolean} valid true if the form has passed client-side validation
9546          */
9547         clientvalidation: true,
9548         /**
9549          * @event beforeaction
9550          * Fires before any action is performed. Return false to cancel the action.
9551          * @param {Form} this
9552          * @param {Action} action The action to be performed
9553          */
9554         beforeaction: true,
9555         /**
9556          * @event actionfailed
9557          * Fires when an action fails.
9558          * @param {Form} this
9559          * @param {Action} action The action that failed
9560          */
9561         actionfailed : true,
9562         /**
9563          * @event actioncomplete
9564          * Fires when an action is completed.
9565          * @param {Form} this
9566          * @param {Action} action The action that completed
9567          */
9568         actioncomplete : true
9569     });
9570 };
9571
9572 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9573
9574      /**
9575      * @cfg {String} method
9576      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9577      */
9578     method : 'POST',
9579     /**
9580      * @cfg {String} url
9581      * The URL to use for form actions if one isn't supplied in the action options.
9582      */
9583     /**
9584      * @cfg {Boolean} fileUpload
9585      * Set to true if this form is a file upload.
9586      */
9587
9588     /**
9589      * @cfg {Object} baseParams
9590      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9591      */
9592
9593     /**
9594      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9595      */
9596     timeout: 30,
9597     /**
9598      * @cfg {Sting} align (left|right) for navbar forms
9599      */
9600     align : 'left',
9601
9602     // private
9603     activeAction : null,
9604
9605     /**
9606      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9607      * element by passing it or its id or mask the form itself by passing in true.
9608      * @type Mixed
9609      */
9610     waitMsgTarget : false,
9611
9612     loadMask : true,
9613     
9614     /**
9615      * @cfg {Boolean} errorMask (true|false) default false
9616      */
9617     errorMask : false,
9618     
9619     /**
9620      * @cfg {Number} maskOffset Default 100
9621      */
9622     maskOffset : 100,
9623     
9624     /**
9625      * @cfg {Boolean} maskBody
9626      */
9627     maskBody : false,
9628
9629     getAutoCreate : function(){
9630
9631         var cfg = {
9632             tag: 'form',
9633             method : this.method || 'POST',
9634             id : this.id || Roo.id(),
9635             cls : ''
9636         };
9637         if (this.parent().xtype.match(/^Nav/)) {
9638             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9639
9640         }
9641
9642         if (this.labelAlign == 'left' ) {
9643             cfg.cls += ' form-horizontal';
9644         }
9645
9646
9647         return cfg;
9648     },
9649     initEvents : function()
9650     {
9651         this.el.on('submit', this.onSubmit, this);
9652         // this was added as random key presses on the form where triggering form submit.
9653         this.el.on('keypress', function(e) {
9654             if (e.getCharCode() != 13) {
9655                 return true;
9656             }
9657             // we might need to allow it for textareas.. and some other items.
9658             // check e.getTarget().
9659
9660             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9661                 return true;
9662             }
9663
9664             Roo.log("keypress blocked");
9665
9666             e.preventDefault();
9667             return false;
9668         });
9669         
9670     },
9671     // private
9672     onSubmit : function(e){
9673         e.stopEvent();
9674     },
9675
9676      /**
9677      * Returns true if client-side validation on the form is successful.
9678      * @return Boolean
9679      */
9680     isValid : function(){
9681         var items = this.getItems();
9682         var valid = true;
9683         var target = false;
9684         
9685         items.each(function(f){
9686             
9687             if(f.validate()){
9688                 return;
9689             }
9690             
9691             Roo.log('invalid field: ' + f.name);
9692             
9693             valid = false;
9694
9695             if(!target && f.el.isVisible(true)){
9696                 target = f;
9697             }
9698            
9699         });
9700         
9701         if(this.errorMask && !valid){
9702             Roo.bootstrap.Form.popover.mask(this, target);
9703         }
9704         
9705         return valid;
9706     },
9707     
9708     /**
9709      * Returns true if any fields in this form have changed since their original load.
9710      * @return Boolean
9711      */
9712     isDirty : function(){
9713         var dirty = false;
9714         var items = this.getItems();
9715         items.each(function(f){
9716            if(f.isDirty()){
9717                dirty = true;
9718                return false;
9719            }
9720            return true;
9721         });
9722         return dirty;
9723     },
9724      /**
9725      * Performs a predefined action (submit or load) or custom actions you define on this form.
9726      * @param {String} actionName The name of the action type
9727      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9728      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9729      * accept other config options):
9730      * <pre>
9731 Property          Type             Description
9732 ----------------  ---------------  ----------------------------------------------------------------------------------
9733 url               String           The url for the action (defaults to the form's url)
9734 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9735 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9736 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9737                                    validate the form on the client (defaults to false)
9738      * </pre>
9739      * @return {BasicForm} this
9740      */
9741     doAction : function(action, options){
9742         if(typeof action == 'string'){
9743             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9744         }
9745         if(this.fireEvent('beforeaction', this, action) !== false){
9746             this.beforeAction(action);
9747             action.run.defer(100, action);
9748         }
9749         return this;
9750     },
9751
9752     // private
9753     beforeAction : function(action){
9754         var o = action.options;
9755         
9756         if(this.loadMask){
9757             
9758             if(this.maskBody){
9759                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9760             } else {
9761                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9762             }
9763         }
9764         // not really supported yet.. ??
9765
9766         //if(this.waitMsgTarget === true){
9767         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9768         //}else if(this.waitMsgTarget){
9769         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9770         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9771         //}else {
9772         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9773        // }
9774
9775     },
9776
9777     // private
9778     afterAction : function(action, success){
9779         this.activeAction = null;
9780         var o = action.options;
9781
9782         if(this.loadMask){
9783             
9784             if(this.maskBody){
9785                 Roo.get(document.body).unmask();
9786             } else {
9787                 this.el.unmask();
9788             }
9789         }
9790         
9791         //if(this.waitMsgTarget === true){
9792 //            this.el.unmask();
9793         //}else if(this.waitMsgTarget){
9794         //    this.waitMsgTarget.unmask();
9795         //}else{
9796         //    Roo.MessageBox.updateProgress(1);
9797         //    Roo.MessageBox.hide();
9798        // }
9799         //
9800         if(success){
9801             if(o.reset){
9802                 this.reset();
9803             }
9804             Roo.callback(o.success, o.scope, [this, action]);
9805             this.fireEvent('actioncomplete', this, action);
9806
9807         }else{
9808
9809             // failure condition..
9810             // we have a scenario where updates need confirming.
9811             // eg. if a locking scenario exists..
9812             // we look for { errors : { needs_confirm : true }} in the response.
9813             if (
9814                 (typeof(action.result) != 'undefined')  &&
9815                 (typeof(action.result.errors) != 'undefined')  &&
9816                 (typeof(action.result.errors.needs_confirm) != 'undefined')
9817            ){
9818                 var _t = this;
9819                 Roo.log("not supported yet");
9820                  /*
9821
9822                 Roo.MessageBox.confirm(
9823                     "Change requires confirmation",
9824                     action.result.errorMsg,
9825                     function(r) {
9826                         if (r != 'yes') {
9827                             return;
9828                         }
9829                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
9830                     }
9831
9832                 );
9833                 */
9834
9835
9836                 return;
9837             }
9838
9839             Roo.callback(o.failure, o.scope, [this, action]);
9840             // show an error message if no failed handler is set..
9841             if (!this.hasListener('actionfailed')) {
9842                 Roo.log("need to add dialog support");
9843                 /*
9844                 Roo.MessageBox.alert("Error",
9845                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9846                         action.result.errorMsg :
9847                         "Saving Failed, please check your entries or try again"
9848                 );
9849                 */
9850             }
9851
9852             this.fireEvent('actionfailed', this, action);
9853         }
9854
9855     },
9856     /**
9857      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9858      * @param {String} id The value to search for
9859      * @return Field
9860      */
9861     findField : function(id){
9862         var items = this.getItems();
9863         var field = items.get(id);
9864         if(!field){
9865              items.each(function(f){
9866                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9867                     field = f;
9868                     return false;
9869                 }
9870                 return true;
9871             });
9872         }
9873         return field || null;
9874     },
9875      /**
9876      * Mark fields in this form invalid in bulk.
9877      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9878      * @return {BasicForm} this
9879      */
9880     markInvalid : function(errors){
9881         if(errors instanceof Array){
9882             for(var i = 0, len = errors.length; i < len; i++){
9883                 var fieldError = errors[i];
9884                 var f = this.findField(fieldError.id);
9885                 if(f){
9886                     f.markInvalid(fieldError.msg);
9887                 }
9888             }
9889         }else{
9890             var field, id;
9891             for(id in errors){
9892                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9893                     field.markInvalid(errors[id]);
9894                 }
9895             }
9896         }
9897         //Roo.each(this.childForms || [], function (f) {
9898         //    f.markInvalid(errors);
9899         //});
9900
9901         return this;
9902     },
9903
9904     /**
9905      * Set values for fields in this form in bulk.
9906      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9907      * @return {BasicForm} this
9908      */
9909     setValues : function(values){
9910         if(values instanceof Array){ // array of objects
9911             for(var i = 0, len = values.length; i < len; i++){
9912                 var v = values[i];
9913                 var f = this.findField(v.id);
9914                 if(f){
9915                     f.setValue(v.value);
9916                     if(this.trackResetOnLoad){
9917                         f.originalValue = f.getValue();
9918                     }
9919                 }
9920             }
9921         }else{ // object hash
9922             var field, id;
9923             for(id in values){
9924                 if(typeof values[id] != 'function' && (field = this.findField(id))){
9925
9926                     if (field.setFromData &&
9927                         field.valueField &&
9928                         field.displayField &&
9929                         // combos' with local stores can
9930                         // be queried via setValue()
9931                         // to set their value..
9932                         (field.store && !field.store.isLocal)
9933                         ) {
9934                         // it's a combo
9935                         var sd = { };
9936                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9937                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9938                         field.setFromData(sd);
9939
9940                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9941                         
9942                         field.setFromData(values);
9943                         
9944                     } else {
9945                         field.setValue(values[id]);
9946                     }
9947
9948
9949                     if(this.trackResetOnLoad){
9950                         field.originalValue = field.getValue();
9951                     }
9952                 }
9953             }
9954         }
9955
9956         //Roo.each(this.childForms || [], function (f) {
9957         //    f.setValues(values);
9958         //});
9959
9960         return this;
9961     },
9962
9963     /**
9964      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9965      * they are returned as an array.
9966      * @param {Boolean} asString
9967      * @return {Object}
9968      */
9969     getValues : function(asString){
9970         //if (this.childForms) {
9971             // copy values from the child forms
9972         //    Roo.each(this.childForms, function (f) {
9973         //        this.setValues(f.getValues());
9974         //    }, this);
9975         //}
9976
9977
9978
9979         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9980         if(asString === true){
9981             return fs;
9982         }
9983         return Roo.urlDecode(fs);
9984     },
9985
9986     /**
9987      * Returns the fields in this form as an object with key/value pairs.
9988      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9989      * @return {Object}
9990      */
9991     getFieldValues : function(with_hidden)
9992     {
9993         var items = this.getItems();
9994         var ret = {};
9995         items.each(function(f){
9996             
9997             if (!f.getName()) {
9998                 return;
9999             }
10000             
10001             var v = f.getValue();
10002             
10003             if (f.inputType =='radio') {
10004                 if (typeof(ret[f.getName()]) == 'undefined') {
10005                     ret[f.getName()] = ''; // empty..
10006                 }
10007
10008                 if (!f.el.dom.checked) {
10009                     return;
10010
10011                 }
10012                 v = f.el.dom.value;
10013
10014             }
10015             
10016             if(f.xtype == 'MoneyField'){
10017                 ret[f.currencyName] = f.getCurrency();
10018             }
10019
10020             // not sure if this supported any more..
10021             if ((typeof(v) == 'object') && f.getRawValue) {
10022                 v = f.getRawValue() ; // dates..
10023             }
10024             // combo boxes where name != hiddenName...
10025             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10026                 ret[f.name] = f.getRawValue();
10027             }
10028             ret[f.getName()] = v;
10029         });
10030
10031         return ret;
10032     },
10033
10034     /**
10035      * Clears all invalid messages in this form.
10036      * @return {BasicForm} this
10037      */
10038     clearInvalid : function(){
10039         var items = this.getItems();
10040
10041         items.each(function(f){
10042            f.clearInvalid();
10043         });
10044
10045         return this;
10046     },
10047
10048     /**
10049      * Resets this form.
10050      * @return {BasicForm} this
10051      */
10052     reset : function(){
10053         var items = this.getItems();
10054         items.each(function(f){
10055             f.reset();
10056         });
10057
10058         Roo.each(this.childForms || [], function (f) {
10059             f.reset();
10060         });
10061
10062
10063         return this;
10064     },
10065     
10066     getItems : function()
10067     {
10068         var r=new Roo.util.MixedCollection(false, function(o){
10069             return o.id || (o.id = Roo.id());
10070         });
10071         var iter = function(el) {
10072             if (el.inputEl) {
10073                 r.add(el);
10074             }
10075             if (!el.items) {
10076                 return;
10077             }
10078             Roo.each(el.items,function(e) {
10079                 iter(e);
10080             });
10081         };
10082
10083         iter(this);
10084         return r;
10085     },
10086     
10087     hideFields : function(items)
10088     {
10089         Roo.each(items, function(i){
10090             
10091             var f = this.findField(i);
10092             
10093             if(!f){
10094                 return;
10095             }
10096             
10097             f.hide();
10098             
10099         }, this);
10100     },
10101     
10102     showFields : function(items)
10103     {
10104         Roo.each(items, function(i){
10105             
10106             var f = this.findField(i);
10107             
10108             if(!f){
10109                 return;
10110             }
10111             
10112             f.show();
10113             
10114         }, this);
10115     }
10116
10117 });
10118
10119 Roo.apply(Roo.bootstrap.Form, {
10120     
10121     popover : {
10122         
10123         padding : 5,
10124         
10125         isApplied : false,
10126         
10127         isMasked : false,
10128         
10129         form : false,
10130         
10131         target : false,
10132         
10133         toolTip : false,
10134         
10135         intervalID : false,
10136         
10137         maskEl : false,
10138         
10139         apply : function()
10140         {
10141             if(this.isApplied){
10142                 return;
10143             }
10144             
10145             this.maskEl = {
10146                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10147                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10148                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10149                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10150             };
10151             
10152             this.maskEl.top.enableDisplayMode("block");
10153             this.maskEl.left.enableDisplayMode("block");
10154             this.maskEl.bottom.enableDisplayMode("block");
10155             this.maskEl.right.enableDisplayMode("block");
10156             
10157             this.toolTip = new Roo.bootstrap.Tooltip({
10158                 cls : 'roo-form-error-popover',
10159                 alignment : {
10160                     'left' : ['r-l', [-2,0], 'right'],
10161                     'right' : ['l-r', [2,0], 'left'],
10162                     'bottom' : ['tl-bl', [0,2], 'top'],
10163                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10164                 }
10165             });
10166             
10167             this.toolTip.render(Roo.get(document.body));
10168
10169             this.toolTip.el.enableDisplayMode("block");
10170             
10171             Roo.get(document.body).on('click', function(){
10172                 this.unmask();
10173             }, this);
10174             
10175             Roo.get(document.body).on('touchstart', function(){
10176                 this.unmask();
10177             }, this);
10178             
10179             this.isApplied = true
10180         },
10181         
10182         mask : function(form, target)
10183         {
10184             this.form = form;
10185             
10186             this.target = target;
10187             
10188             if(!this.form.errorMask || !target.el){
10189                 return;
10190             }
10191             
10192             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10193             
10194             Roo.log(scrollable);
10195             
10196             var ot = this.target.el.calcOffsetsTo(scrollable);
10197             
10198             var scrollTo = ot[1] - this.form.maskOffset;
10199             
10200             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10201             
10202             scrollable.scrollTo('top', scrollTo);
10203             
10204             var box = this.target.el.getBox();
10205             Roo.log(box);
10206             var zIndex = Roo.bootstrap.Modal.zIndex++;
10207
10208             
10209             this.maskEl.top.setStyle('position', 'absolute');
10210             this.maskEl.top.setStyle('z-index', zIndex);
10211             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10212             this.maskEl.top.setLeft(0);
10213             this.maskEl.top.setTop(0);
10214             this.maskEl.top.show();
10215             
10216             this.maskEl.left.setStyle('position', 'absolute');
10217             this.maskEl.left.setStyle('z-index', zIndex);
10218             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10219             this.maskEl.left.setLeft(0);
10220             this.maskEl.left.setTop(box.y - this.padding);
10221             this.maskEl.left.show();
10222
10223             this.maskEl.bottom.setStyle('position', 'absolute');
10224             this.maskEl.bottom.setStyle('z-index', zIndex);
10225             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10226             this.maskEl.bottom.setLeft(0);
10227             this.maskEl.bottom.setTop(box.bottom + this.padding);
10228             this.maskEl.bottom.show();
10229
10230             this.maskEl.right.setStyle('position', 'absolute');
10231             this.maskEl.right.setStyle('z-index', zIndex);
10232             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10233             this.maskEl.right.setLeft(box.right + this.padding);
10234             this.maskEl.right.setTop(box.y - this.padding);
10235             this.maskEl.right.show();
10236
10237             this.toolTip.bindEl = this.target.el;
10238
10239             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10240
10241             var tip = this.target.blankText;
10242
10243             if(this.target.getValue() !== '' ) {
10244                 
10245                 if (this.target.invalidText.length) {
10246                     tip = this.target.invalidText;
10247                 } else if (this.target.regexText.length){
10248                     tip = this.target.regexText;
10249                 }
10250             }
10251
10252             this.toolTip.show(tip);
10253
10254             this.intervalID = window.setInterval(function() {
10255                 Roo.bootstrap.Form.popover.unmask();
10256             }, 10000);
10257
10258             window.onwheel = function(){ return false;};
10259             
10260             (function(){ this.isMasked = true; }).defer(500, this);
10261             
10262         },
10263         
10264         unmask : function()
10265         {
10266             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10267                 return;
10268             }
10269             
10270             this.maskEl.top.setStyle('position', 'absolute');
10271             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10272             this.maskEl.top.hide();
10273
10274             this.maskEl.left.setStyle('position', 'absolute');
10275             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10276             this.maskEl.left.hide();
10277
10278             this.maskEl.bottom.setStyle('position', 'absolute');
10279             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10280             this.maskEl.bottom.hide();
10281
10282             this.maskEl.right.setStyle('position', 'absolute');
10283             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10284             this.maskEl.right.hide();
10285             
10286             this.toolTip.hide();
10287             
10288             this.toolTip.el.hide();
10289             
10290             window.onwheel = function(){ return true;};
10291             
10292             if(this.intervalID){
10293                 window.clearInterval(this.intervalID);
10294                 this.intervalID = false;
10295             }
10296             
10297             this.isMasked = false;
10298             
10299         }
10300         
10301     }
10302     
10303 });
10304
10305 /*
10306  * Based on:
10307  * Ext JS Library 1.1.1
10308  * Copyright(c) 2006-2007, Ext JS, LLC.
10309  *
10310  * Originally Released Under LGPL - original licence link has changed is not relivant.
10311  *
10312  * Fork - LGPL
10313  * <script type="text/javascript">
10314  */
10315 /**
10316  * @class Roo.form.VTypes
10317  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10318  * @singleton
10319  */
10320 Roo.form.VTypes = function(){
10321     // closure these in so they are only created once.
10322     var alpha = /^[a-zA-Z_]+$/;
10323     var alphanum = /^[a-zA-Z0-9_]+$/;
10324     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10325     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10326
10327     // All these messages and functions are configurable
10328     return {
10329         /**
10330          * The function used to validate email addresses
10331          * @param {String} value The email address
10332          */
10333         'email' : function(v){
10334             return email.test(v);
10335         },
10336         /**
10337          * The error text to display when the email validation function returns false
10338          * @type String
10339          */
10340         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10341         /**
10342          * The keystroke filter mask to be applied on email input
10343          * @type RegExp
10344          */
10345         'emailMask' : /[a-z0-9_\.\-@]/i,
10346
10347         /**
10348          * The function used to validate URLs
10349          * @param {String} value The URL
10350          */
10351         'url' : function(v){
10352             return url.test(v);
10353         },
10354         /**
10355          * The error text to display when the url validation function returns false
10356          * @type String
10357          */
10358         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10359         
10360         /**
10361          * The function used to validate alpha values
10362          * @param {String} value The value
10363          */
10364         'alpha' : function(v){
10365             return alpha.test(v);
10366         },
10367         /**
10368          * The error text to display when the alpha validation function returns false
10369          * @type String
10370          */
10371         'alphaText' : 'This field should only contain letters and _',
10372         /**
10373          * The keystroke filter mask to be applied on alpha input
10374          * @type RegExp
10375          */
10376         'alphaMask' : /[a-z_]/i,
10377
10378         /**
10379          * The function used to validate alphanumeric values
10380          * @param {String} value The value
10381          */
10382         'alphanum' : function(v){
10383             return alphanum.test(v);
10384         },
10385         /**
10386          * The error text to display when the alphanumeric validation function returns false
10387          * @type String
10388          */
10389         'alphanumText' : 'This field should only contain letters, numbers and _',
10390         /**
10391          * The keystroke filter mask to be applied on alphanumeric input
10392          * @type RegExp
10393          */
10394         'alphanumMask' : /[a-z0-9_]/i
10395     };
10396 }();/*
10397  * - LGPL
10398  *
10399  * Input
10400  * 
10401  */
10402
10403 /**
10404  * @class Roo.bootstrap.Input
10405  * @extends Roo.bootstrap.Component
10406  * Bootstrap Input class
10407  * @cfg {Boolean} disabled is it disabled
10408  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10409  * @cfg {String} name name of the input
10410  * @cfg {string} fieldLabel - the label associated
10411  * @cfg {string} placeholder - placeholder to put in text.
10412  * @cfg {string}  before - input group add on before
10413  * @cfg {string} after - input group add on after
10414  * @cfg {string} size - (lg|sm) or leave empty..
10415  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10416  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10417  * @cfg {Number} md colspan out of 12 for computer-sized screens
10418  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10419  * @cfg {string} value default value of the input
10420  * @cfg {Number} labelWidth set the width of label 
10421  * @cfg {Number} labellg set the width of label (1-12)
10422  * @cfg {Number} labelmd set the width of label (1-12)
10423  * @cfg {Number} labelsm set the width of label (1-12)
10424  * @cfg {Number} labelxs set the width of label (1-12)
10425  * @cfg {String} labelAlign (top|left)
10426  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10427  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10428  * @cfg {String} indicatorpos (left|right) default left
10429  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10430  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10431  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10432
10433  * @cfg {String} align (left|center|right) Default left
10434  * @cfg {Boolean} forceFeedback (true|false) Default false
10435  * 
10436  * @constructor
10437  * Create a new Input
10438  * @param {Object} config The config object
10439  */
10440
10441 Roo.bootstrap.Input = function(config){
10442     
10443     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10444     
10445     this.addEvents({
10446         /**
10447          * @event focus
10448          * Fires when this field receives input focus.
10449          * @param {Roo.form.Field} this
10450          */
10451         focus : true,
10452         /**
10453          * @event blur
10454          * Fires when this field loses input focus.
10455          * @param {Roo.form.Field} this
10456          */
10457         blur : true,
10458         /**
10459          * @event specialkey
10460          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10461          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10462          * @param {Roo.form.Field} this
10463          * @param {Roo.EventObject} e The event object
10464          */
10465         specialkey : true,
10466         /**
10467          * @event change
10468          * Fires just before the field blurs if the field value has changed.
10469          * @param {Roo.form.Field} this
10470          * @param {Mixed} newValue The new value
10471          * @param {Mixed} oldValue The original value
10472          */
10473         change : true,
10474         /**
10475          * @event invalid
10476          * Fires after the field has been marked as invalid.
10477          * @param {Roo.form.Field} this
10478          * @param {String} msg The validation message
10479          */
10480         invalid : true,
10481         /**
10482          * @event valid
10483          * Fires after the field has been validated with no errors.
10484          * @param {Roo.form.Field} this
10485          */
10486         valid : true,
10487          /**
10488          * @event keyup
10489          * Fires after the key up
10490          * @param {Roo.form.Field} this
10491          * @param {Roo.EventObject}  e The event Object
10492          */
10493         keyup : true
10494     });
10495 };
10496
10497 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10498      /**
10499      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10500       automatic validation (defaults to "keyup").
10501      */
10502     validationEvent : "keyup",
10503      /**
10504      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10505      */
10506     validateOnBlur : true,
10507     /**
10508      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10509      */
10510     validationDelay : 250,
10511      /**
10512      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10513      */
10514     focusClass : "x-form-focus",  // not needed???
10515     
10516        
10517     /**
10518      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10519      */
10520     invalidClass : "has-warning",
10521     
10522     /**
10523      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10524      */
10525     validClass : "has-success",
10526     
10527     /**
10528      * @cfg {Boolean} hasFeedback (true|false) default true
10529      */
10530     hasFeedback : true,
10531     
10532     /**
10533      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10534      */
10535     invalidFeedbackClass : "glyphicon-warning-sign",
10536     
10537     /**
10538      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10539      */
10540     validFeedbackClass : "glyphicon-ok",
10541     
10542     /**
10543      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10544      */
10545     selectOnFocus : false,
10546     
10547      /**
10548      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10549      */
10550     maskRe : null,
10551        /**
10552      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10553      */
10554     vtype : null,
10555     
10556       /**
10557      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10558      */
10559     disableKeyFilter : false,
10560     
10561        /**
10562      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10563      */
10564     disabled : false,
10565      /**
10566      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10567      */
10568     allowBlank : true,
10569     /**
10570      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10571      */
10572     blankText : "Please complete this mandatory field",
10573     
10574      /**
10575      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10576      */
10577     minLength : 0,
10578     /**
10579      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10580      */
10581     maxLength : Number.MAX_VALUE,
10582     /**
10583      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10584      */
10585     minLengthText : "The minimum length for this field is {0}",
10586     /**
10587      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10588      */
10589     maxLengthText : "The maximum length for this field is {0}",
10590   
10591     
10592     /**
10593      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10594      * If available, this function will be called only after the basic validators all return true, and will be passed the
10595      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10596      */
10597     validator : null,
10598     /**
10599      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10600      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10601      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10602      */
10603     regex : null,
10604     /**
10605      * @cfg {String} regexText -- Depricated - use Invalid Text
10606      */
10607     regexText : "",
10608     
10609     /**
10610      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10611      */
10612     invalidText : "",
10613     
10614     
10615     
10616     autocomplete: false,
10617     
10618     
10619     fieldLabel : '',
10620     inputType : 'text',
10621     
10622     name : false,
10623     placeholder: false,
10624     before : false,
10625     after : false,
10626     size : false,
10627     hasFocus : false,
10628     preventMark: false,
10629     isFormField : true,
10630     value : '',
10631     labelWidth : 2,
10632     labelAlign : false,
10633     readOnly : false,
10634     align : false,
10635     formatedValue : false,
10636     forceFeedback : false,
10637     
10638     indicatorpos : 'left',
10639     
10640     labellg : 0,
10641     labelmd : 0,
10642     labelsm : 0,
10643     labelxs : 0,
10644     
10645     capture : '',
10646     accept : '',
10647     
10648     parentLabelAlign : function()
10649     {
10650         var parent = this;
10651         while (parent.parent()) {
10652             parent = parent.parent();
10653             if (typeof(parent.labelAlign) !='undefined') {
10654                 return parent.labelAlign;
10655             }
10656         }
10657         return 'left';
10658         
10659     },
10660     
10661     getAutoCreate : function()
10662     {
10663         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10664         
10665         var id = Roo.id();
10666         
10667         var cfg = {};
10668         
10669         if(this.inputType != 'hidden'){
10670             cfg.cls = 'form-group' //input-group
10671         }
10672         
10673         var input =  {
10674             tag: 'input',
10675             id : id,
10676             type : this.inputType,
10677             value : this.value,
10678             cls : 'form-control',
10679             placeholder : this.placeholder || '',
10680             autocomplete : this.autocomplete || 'new-password'
10681         };
10682         if (this.inputType == 'file') {
10683             input.style = 'overflow:hidden'; // why not in CSS?
10684         }
10685         
10686         if(this.capture.length){
10687             input.capture = this.capture;
10688         }
10689         
10690         if(this.accept.length){
10691             input.accept = this.accept + "/*";
10692         }
10693         
10694         if(this.align){
10695             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10696         }
10697         
10698         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10699             input.maxLength = this.maxLength;
10700         }
10701         
10702         if (this.disabled) {
10703             input.disabled=true;
10704         }
10705         
10706         if (this.readOnly) {
10707             input.readonly=true;
10708         }
10709         
10710         if (this.name) {
10711             input.name = this.name;
10712         }
10713         
10714         if (this.size) {
10715             input.cls += ' input-' + this.size;
10716         }
10717         
10718         var settings=this;
10719         ['xs','sm','md','lg'].map(function(size){
10720             if (settings[size]) {
10721                 cfg.cls += ' col-' + size + '-' + settings[size];
10722             }
10723         });
10724         
10725         var inputblock = input;
10726         
10727         var feedback = {
10728             tag: 'span',
10729             cls: 'glyphicon form-control-feedback'
10730         };
10731             
10732         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10733             
10734             inputblock = {
10735                 cls : 'has-feedback',
10736                 cn :  [
10737                     input,
10738                     feedback
10739                 ] 
10740             };  
10741         }
10742         
10743         if (this.before || this.after) {
10744             
10745             inputblock = {
10746                 cls : 'input-group',
10747                 cn :  [] 
10748             };
10749             
10750             if (this.before && typeof(this.before) == 'string') {
10751                 
10752                 inputblock.cn.push({
10753                     tag :'span',
10754                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10755                     html : this.before
10756                 });
10757             }
10758             if (this.before && typeof(this.before) == 'object') {
10759                 this.before = Roo.factory(this.before);
10760                 
10761                 inputblock.cn.push({
10762                     tag :'span',
10763                     cls : 'roo-input-before input-group-prepend   input-group-' +
10764                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10765                 });
10766             }
10767             
10768             inputblock.cn.push(input);
10769             
10770             if (this.after && typeof(this.after) == 'string') {
10771                 inputblock.cn.push({
10772                     tag :'span',
10773                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10774                     html : this.after
10775                 });
10776             }
10777             if (this.after && typeof(this.after) == 'object') {
10778                 this.after = Roo.factory(this.after);
10779                 
10780                 inputblock.cn.push({
10781                     tag :'span',
10782                     cls : 'roo-input-after input-group-append  input-group-' +
10783                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10784                 });
10785             }
10786             
10787             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10788                 inputblock.cls += ' has-feedback';
10789                 inputblock.cn.push(feedback);
10790             }
10791         };
10792         var indicator = {
10793             tag : 'i',
10794             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10795             tooltip : 'This field is required'
10796         };
10797         if (this.allowBlank ) {
10798             indicator.style = this.allowBlank ? ' display:none' : '';
10799         }
10800         if (align ==='left' && this.fieldLabel.length) {
10801             
10802             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10803             
10804             cfg.cn = [
10805                 indicator,
10806                 {
10807                     tag: 'label',
10808                     'for' :  id,
10809                     cls : 'control-label col-form-label',
10810                     html : this.fieldLabel
10811
10812                 },
10813                 {
10814                     cls : "", 
10815                     cn: [
10816                         inputblock
10817                     ]
10818                 }
10819             ];
10820             
10821             var labelCfg = cfg.cn[1];
10822             var contentCfg = cfg.cn[2];
10823             
10824             if(this.indicatorpos == 'right'){
10825                 cfg.cn = [
10826                     {
10827                         tag: 'label',
10828                         'for' :  id,
10829                         cls : 'control-label col-form-label',
10830                         cn : [
10831                             {
10832                                 tag : 'span',
10833                                 html : this.fieldLabel
10834                             },
10835                             indicator
10836                         ]
10837                     },
10838                     {
10839                         cls : "",
10840                         cn: [
10841                             inputblock
10842                         ]
10843                     }
10844
10845                 ];
10846                 
10847                 labelCfg = cfg.cn[0];
10848                 contentCfg = cfg.cn[1];
10849             
10850             }
10851             
10852             if(this.labelWidth > 12){
10853                 labelCfg.style = "width: " + this.labelWidth + 'px';
10854             }
10855             
10856             if(this.labelWidth < 13 && this.labelmd == 0){
10857                 this.labelmd = this.labelWidth;
10858             }
10859             
10860             if(this.labellg > 0){
10861                 labelCfg.cls += ' col-lg-' + this.labellg;
10862                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10863             }
10864             
10865             if(this.labelmd > 0){
10866                 labelCfg.cls += ' col-md-' + this.labelmd;
10867                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10868             }
10869             
10870             if(this.labelsm > 0){
10871                 labelCfg.cls += ' col-sm-' + this.labelsm;
10872                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10873             }
10874             
10875             if(this.labelxs > 0){
10876                 labelCfg.cls += ' col-xs-' + this.labelxs;
10877                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10878             }
10879             
10880             
10881         } else if ( this.fieldLabel.length) {
10882                 
10883             
10884             
10885             cfg.cn = [
10886                 {
10887                     tag : 'i',
10888                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10889                     tooltip : 'This field is required',
10890                     style : this.allowBlank ? ' display:none' : '' 
10891                 },
10892                 {
10893                     tag: 'label',
10894                    //cls : 'input-group-addon',
10895                     html : this.fieldLabel
10896
10897                 },
10898
10899                inputblock
10900
10901            ];
10902            
10903            if(this.indicatorpos == 'right'){
10904        
10905                 cfg.cn = [
10906                     {
10907                         tag: 'label',
10908                        //cls : 'input-group-addon',
10909                         html : this.fieldLabel
10910
10911                     },
10912                     {
10913                         tag : 'i',
10914                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10915                         tooltip : 'This field is required',
10916                         style : this.allowBlank ? ' display:none' : '' 
10917                     },
10918
10919                    inputblock
10920
10921                ];
10922
10923             }
10924
10925         } else {
10926             
10927             cfg.cn = [
10928
10929                     inputblock
10930
10931             ];
10932                 
10933                 
10934         };
10935         
10936         if (this.parentType === 'Navbar' &&  this.parent().bar) {
10937            cfg.cls += ' navbar-form';
10938         }
10939         
10940         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10941             // on BS4 we do this only if not form 
10942             cfg.cls += ' navbar-form';
10943             cfg.tag = 'li';
10944         }
10945         
10946         return cfg;
10947         
10948     },
10949     /**
10950      * return the real input element.
10951      */
10952     inputEl: function ()
10953     {
10954         return this.el.select('input.form-control',true).first();
10955     },
10956     
10957     tooltipEl : function()
10958     {
10959         return this.inputEl();
10960     },
10961     
10962     indicatorEl : function()
10963     {
10964         if (Roo.bootstrap.version == 4) {
10965             return false; // not enabled in v4 yet.
10966         }
10967         
10968         var indicator = this.el.select('i.roo-required-indicator',true).first();
10969         
10970         if(!indicator){
10971             return false;
10972         }
10973         
10974         return indicator;
10975         
10976     },
10977     
10978     setDisabled : function(v)
10979     {
10980         var i  = this.inputEl().dom;
10981         if (!v) {
10982             i.removeAttribute('disabled');
10983             return;
10984             
10985         }
10986         i.setAttribute('disabled','true');
10987     },
10988     initEvents : function()
10989     {
10990           
10991         this.inputEl().on("keydown" , this.fireKey,  this);
10992         this.inputEl().on("focus", this.onFocus,  this);
10993         this.inputEl().on("blur", this.onBlur,  this);
10994         
10995         this.inputEl().relayEvent('keyup', this);
10996         
10997         this.indicator = this.indicatorEl();
10998         
10999         if(this.indicator){
11000             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
11001         }
11002  
11003         // reference to original value for reset
11004         this.originalValue = this.getValue();
11005         //Roo.form.TextField.superclass.initEvents.call(this);
11006         if(this.validationEvent == 'keyup'){
11007             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11008             this.inputEl().on('keyup', this.filterValidation, this);
11009         }
11010         else if(this.validationEvent !== false){
11011             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11012         }
11013         
11014         if(this.selectOnFocus){
11015             this.on("focus", this.preFocus, this);
11016             
11017         }
11018         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11019             this.inputEl().on("keypress", this.filterKeys, this);
11020         } else {
11021             this.inputEl().relayEvent('keypress', this);
11022         }
11023        /* if(this.grow){
11024             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11025             this.el.on("click", this.autoSize,  this);
11026         }
11027         */
11028         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11029             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11030         }
11031         
11032         if (typeof(this.before) == 'object') {
11033             this.before.render(this.el.select('.roo-input-before',true).first());
11034         }
11035         if (typeof(this.after) == 'object') {
11036             this.after.render(this.el.select('.roo-input-after',true).first());
11037         }
11038         
11039         this.inputEl().on('change', this.onChange, this);
11040         
11041     },
11042     filterValidation : function(e){
11043         if(!e.isNavKeyPress()){
11044             this.validationTask.delay(this.validationDelay);
11045         }
11046     },
11047      /**
11048      * Validates the field value
11049      * @return {Boolean} True if the value is valid, else false
11050      */
11051     validate : function(){
11052         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11053         if(this.disabled || this.validateValue(this.getRawValue())){
11054             this.markValid();
11055             return true;
11056         }
11057         
11058         this.markInvalid();
11059         return false;
11060     },
11061     
11062     
11063     /**
11064      * Validates a value according to the field's validation rules and marks the field as invalid
11065      * if the validation fails
11066      * @param {Mixed} value The value to validate
11067      * @return {Boolean} True if the value is valid, else false
11068      */
11069     validateValue : function(value)
11070     {
11071         if(this.getVisibilityEl().hasClass('hidden')){
11072             return true;
11073         }
11074         
11075         if(value.length < 1)  { // if it's blank
11076             if(this.allowBlank){
11077                 return true;
11078             }
11079             return false;
11080         }
11081         
11082         if(value.length < this.minLength){
11083             return false;
11084         }
11085         if(value.length > this.maxLength){
11086             return false;
11087         }
11088         if(this.vtype){
11089             var vt = Roo.form.VTypes;
11090             if(!vt[this.vtype](value, this)){
11091                 return false;
11092             }
11093         }
11094         if(typeof this.validator == "function"){
11095             var msg = this.validator(value);
11096             if(msg !== true){
11097                 return false;
11098             }
11099             if (typeof(msg) == 'string') {
11100                 this.invalidText = msg;
11101             }
11102         }
11103         
11104         if(this.regex && !this.regex.test(value)){
11105             return false;
11106         }
11107         
11108         return true;
11109     },
11110     
11111      // private
11112     fireKey : function(e){
11113         //Roo.log('field ' + e.getKey());
11114         if(e.isNavKeyPress()){
11115             this.fireEvent("specialkey", this, e);
11116         }
11117     },
11118     focus : function (selectText){
11119         if(this.rendered){
11120             this.inputEl().focus();
11121             if(selectText === true){
11122                 this.inputEl().dom.select();
11123             }
11124         }
11125         return this;
11126     } ,
11127     
11128     onFocus : function(){
11129         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11130            // this.el.addClass(this.focusClass);
11131         }
11132         if(!this.hasFocus){
11133             this.hasFocus = true;
11134             this.startValue = this.getValue();
11135             this.fireEvent("focus", this);
11136         }
11137     },
11138     
11139     beforeBlur : Roo.emptyFn,
11140
11141     
11142     // private
11143     onBlur : function(){
11144         this.beforeBlur();
11145         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11146             //this.el.removeClass(this.focusClass);
11147         }
11148         this.hasFocus = false;
11149         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11150             this.validate();
11151         }
11152         var v = this.getValue();
11153         if(String(v) !== String(this.startValue)){
11154             this.fireEvent('change', this, v, this.startValue);
11155         }
11156         this.fireEvent("blur", this);
11157     },
11158     
11159     onChange : function(e)
11160     {
11161         var v = this.getValue();
11162         if(String(v) !== String(this.startValue)){
11163             this.fireEvent('change', this, v, this.startValue);
11164         }
11165         
11166     },
11167     
11168     /**
11169      * Resets the current field value to the originally loaded value and clears any validation messages
11170      */
11171     reset : function(){
11172         this.setValue(this.originalValue);
11173         this.validate();
11174     },
11175      /**
11176      * Returns the name of the field
11177      * @return {Mixed} name The name field
11178      */
11179     getName: function(){
11180         return this.name;
11181     },
11182      /**
11183      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11184      * @return {Mixed} value The field value
11185      */
11186     getValue : function(){
11187         
11188         var v = this.inputEl().getValue();
11189         
11190         return v;
11191     },
11192     /**
11193      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11194      * @return {Mixed} value The field value
11195      */
11196     getRawValue : function(){
11197         var v = this.inputEl().getValue();
11198         
11199         return v;
11200     },
11201     
11202     /**
11203      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11204      * @param {Mixed} value The value to set
11205      */
11206     setRawValue : function(v){
11207         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11208     },
11209     
11210     selectText : function(start, end){
11211         var v = this.getRawValue();
11212         if(v.length > 0){
11213             start = start === undefined ? 0 : start;
11214             end = end === undefined ? v.length : end;
11215             var d = this.inputEl().dom;
11216             if(d.setSelectionRange){
11217                 d.setSelectionRange(start, end);
11218             }else if(d.createTextRange){
11219                 var range = d.createTextRange();
11220                 range.moveStart("character", start);
11221                 range.moveEnd("character", v.length-end);
11222                 range.select();
11223             }
11224         }
11225     },
11226     
11227     /**
11228      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11229      * @param {Mixed} value The value to set
11230      */
11231     setValue : function(v){
11232         this.value = v;
11233         if(this.rendered){
11234             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11235             this.validate();
11236         }
11237     },
11238     
11239     /*
11240     processValue : function(value){
11241         if(this.stripCharsRe){
11242             var newValue = value.replace(this.stripCharsRe, '');
11243             if(newValue !== value){
11244                 this.setRawValue(newValue);
11245                 return newValue;
11246             }
11247         }
11248         return value;
11249     },
11250   */
11251     preFocus : function(){
11252         
11253         if(this.selectOnFocus){
11254             this.inputEl().dom.select();
11255         }
11256     },
11257     filterKeys : function(e){
11258         var k = e.getKey();
11259         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11260             return;
11261         }
11262         var c = e.getCharCode(), cc = String.fromCharCode(c);
11263         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11264             return;
11265         }
11266         if(!this.maskRe.test(cc)){
11267             e.stopEvent();
11268         }
11269     },
11270      /**
11271      * Clear any invalid styles/messages for this field
11272      */
11273     clearInvalid : function(){
11274         
11275         if(!this.el || this.preventMark){ // not rendered
11276             return;
11277         }
11278         
11279         
11280         this.el.removeClass([this.invalidClass, 'is-invalid']);
11281         
11282         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11283             
11284             var feedback = this.el.select('.form-control-feedback', true).first();
11285             
11286             if(feedback){
11287                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11288             }
11289             
11290         }
11291         
11292         if(this.indicator){
11293             this.indicator.removeClass('visible');
11294             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11295         }
11296         
11297         this.fireEvent('valid', this);
11298     },
11299     
11300      /**
11301      * Mark this field as valid
11302      */
11303     markValid : function()
11304     {
11305         if(!this.el  || this.preventMark){ // not rendered...
11306             return;
11307         }
11308         
11309         this.el.removeClass([this.invalidClass, this.validClass]);
11310         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11311
11312         var feedback = this.el.select('.form-control-feedback', true).first();
11313             
11314         if(feedback){
11315             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11316         }
11317         
11318         if(this.indicator){
11319             this.indicator.removeClass('visible');
11320             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11321         }
11322         
11323         if(this.disabled){
11324             return;
11325         }
11326         
11327            
11328         if(this.allowBlank && !this.getRawValue().length){
11329             return;
11330         }
11331         if (Roo.bootstrap.version == 3) {
11332             this.el.addClass(this.validClass);
11333         } else {
11334             this.inputEl().addClass('is-valid');
11335         }
11336
11337         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11338             
11339             var feedback = this.el.select('.form-control-feedback', true).first();
11340             
11341             if(feedback){
11342                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11343                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11344             }
11345             
11346         }
11347         
11348         this.fireEvent('valid', this);
11349     },
11350     
11351      /**
11352      * Mark this field as invalid
11353      * @param {String} msg The validation message
11354      */
11355     markInvalid : function(msg)
11356     {
11357         if(!this.el  || this.preventMark){ // not rendered
11358             return;
11359         }
11360         
11361         this.el.removeClass([this.invalidClass, this.validClass]);
11362         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11363         
11364         var feedback = this.el.select('.form-control-feedback', true).first();
11365             
11366         if(feedback){
11367             this.el.select('.form-control-feedback', true).first().removeClass(
11368                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11369         }
11370
11371         if(this.disabled){
11372             return;
11373         }
11374         
11375         if(this.allowBlank && !this.getRawValue().length){
11376             return;
11377         }
11378         
11379         if(this.indicator){
11380             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11381             this.indicator.addClass('visible');
11382         }
11383         if (Roo.bootstrap.version == 3) {
11384             this.el.addClass(this.invalidClass);
11385         } else {
11386             this.inputEl().addClass('is-invalid');
11387         }
11388         
11389         
11390         
11391         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11392             
11393             var feedback = this.el.select('.form-control-feedback', true).first();
11394             
11395             if(feedback){
11396                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11397                 
11398                 if(this.getValue().length || this.forceFeedback){
11399                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11400                 }
11401                 
11402             }
11403             
11404         }
11405         
11406         this.fireEvent('invalid', this, msg);
11407     },
11408     // private
11409     SafariOnKeyDown : function(event)
11410     {
11411         // this is a workaround for a password hang bug on chrome/ webkit.
11412         if (this.inputEl().dom.type != 'password') {
11413             return;
11414         }
11415         
11416         var isSelectAll = false;
11417         
11418         if(this.inputEl().dom.selectionEnd > 0){
11419             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11420         }
11421         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11422             event.preventDefault();
11423             this.setValue('');
11424             return;
11425         }
11426         
11427         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11428             
11429             event.preventDefault();
11430             // this is very hacky as keydown always get's upper case.
11431             //
11432             var cc = String.fromCharCode(event.getCharCode());
11433             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11434             
11435         }
11436     },
11437     adjustWidth : function(tag, w){
11438         tag = tag.toLowerCase();
11439         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11440             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11441                 if(tag == 'input'){
11442                     return w + 2;
11443                 }
11444                 if(tag == 'textarea'){
11445                     return w-2;
11446                 }
11447             }else if(Roo.isOpera){
11448                 if(tag == 'input'){
11449                     return w + 2;
11450                 }
11451                 if(tag == 'textarea'){
11452                     return w-2;
11453                 }
11454             }
11455         }
11456         return w;
11457     },
11458     
11459     setFieldLabel : function(v)
11460     {
11461         if(!this.rendered){
11462             return;
11463         }
11464         
11465         if(this.indicatorEl()){
11466             var ar = this.el.select('label > span',true);
11467             
11468             if (ar.elements.length) {
11469                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11470                 this.fieldLabel = v;
11471                 return;
11472             }
11473             
11474             var br = this.el.select('label',true);
11475             
11476             if(br.elements.length) {
11477                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11478                 this.fieldLabel = v;
11479                 return;
11480             }
11481             
11482             Roo.log('Cannot Found any of label > span || label in input');
11483             return;
11484         }
11485         
11486         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11487         this.fieldLabel = v;
11488         
11489         
11490     }
11491 });
11492
11493  
11494 /*
11495  * - LGPL
11496  *
11497  * Input
11498  * 
11499  */
11500
11501 /**
11502  * @class Roo.bootstrap.TextArea
11503  * @extends Roo.bootstrap.Input
11504  * Bootstrap TextArea class
11505  * @cfg {Number} cols Specifies the visible width of a text area
11506  * @cfg {Number} rows Specifies the visible number of lines in a text area
11507  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11508  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11509  * @cfg {string} html text
11510  * 
11511  * @constructor
11512  * Create a new TextArea
11513  * @param {Object} config The config object
11514  */
11515
11516 Roo.bootstrap.TextArea = function(config){
11517     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11518    
11519 };
11520
11521 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11522      
11523     cols : false,
11524     rows : 5,
11525     readOnly : false,
11526     warp : 'soft',
11527     resize : false,
11528     value: false,
11529     html: false,
11530     
11531     getAutoCreate : function(){
11532         
11533         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11534         
11535         var id = Roo.id();
11536         
11537         var cfg = {};
11538         
11539         if(this.inputType != 'hidden'){
11540             cfg.cls = 'form-group' //input-group
11541         }
11542         
11543         var input =  {
11544             tag: 'textarea',
11545             id : id,
11546             warp : this.warp,
11547             rows : this.rows,
11548             value : this.value || '',
11549             html: this.html || '',
11550             cls : 'form-control',
11551             placeholder : this.placeholder || '' 
11552             
11553         };
11554         
11555         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11556             input.maxLength = this.maxLength;
11557         }
11558         
11559         if(this.resize){
11560             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11561         }
11562         
11563         if(this.cols){
11564             input.cols = this.cols;
11565         }
11566         
11567         if (this.readOnly) {
11568             input.readonly = true;
11569         }
11570         
11571         if (this.name) {
11572             input.name = this.name;
11573         }
11574         
11575         if (this.size) {
11576             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11577         }
11578         
11579         var settings=this;
11580         ['xs','sm','md','lg'].map(function(size){
11581             if (settings[size]) {
11582                 cfg.cls += ' col-' + size + '-' + settings[size];
11583             }
11584         });
11585         
11586         var inputblock = input;
11587         
11588         if(this.hasFeedback && !this.allowBlank){
11589             
11590             var feedback = {
11591                 tag: 'span',
11592                 cls: 'glyphicon form-control-feedback'
11593             };
11594
11595             inputblock = {
11596                 cls : 'has-feedback',
11597                 cn :  [
11598                     input,
11599                     feedback
11600                 ] 
11601             };  
11602         }
11603         
11604         
11605         if (this.before || this.after) {
11606             
11607             inputblock = {
11608                 cls : 'input-group',
11609                 cn :  [] 
11610             };
11611             if (this.before) {
11612                 inputblock.cn.push({
11613                     tag :'span',
11614                     cls : 'input-group-addon',
11615                     html : this.before
11616                 });
11617             }
11618             
11619             inputblock.cn.push(input);
11620             
11621             if(this.hasFeedback && !this.allowBlank){
11622                 inputblock.cls += ' has-feedback';
11623                 inputblock.cn.push(feedback);
11624             }
11625             
11626             if (this.after) {
11627                 inputblock.cn.push({
11628                     tag :'span',
11629                     cls : 'input-group-addon',
11630                     html : this.after
11631                 });
11632             }
11633             
11634         }
11635         
11636         if (align ==='left' && this.fieldLabel.length) {
11637             cfg.cn = [
11638                 {
11639                     tag: 'label',
11640                     'for' :  id,
11641                     cls : 'control-label',
11642                     html : this.fieldLabel
11643                 },
11644                 {
11645                     cls : "",
11646                     cn: [
11647                         inputblock
11648                     ]
11649                 }
11650
11651             ];
11652             
11653             if(this.labelWidth > 12){
11654                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11655             }
11656
11657             if(this.labelWidth < 13 && this.labelmd == 0){
11658                 this.labelmd = this.labelWidth;
11659             }
11660
11661             if(this.labellg > 0){
11662                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11663                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11664             }
11665
11666             if(this.labelmd > 0){
11667                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11668                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11669             }
11670
11671             if(this.labelsm > 0){
11672                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11673                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11674             }
11675
11676             if(this.labelxs > 0){
11677                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11678                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11679             }
11680             
11681         } else if ( this.fieldLabel.length) {
11682             cfg.cn = [
11683
11684                {
11685                    tag: 'label',
11686                    //cls : 'input-group-addon',
11687                    html : this.fieldLabel
11688
11689                },
11690
11691                inputblock
11692
11693            ];
11694
11695         } else {
11696
11697             cfg.cn = [
11698
11699                 inputblock
11700
11701             ];
11702                 
11703         }
11704         
11705         if (this.disabled) {
11706             input.disabled=true;
11707         }
11708         
11709         return cfg;
11710         
11711     },
11712     /**
11713      * return the real textarea element.
11714      */
11715     inputEl: function ()
11716     {
11717         return this.el.select('textarea.form-control',true).first();
11718     },
11719     
11720     /**
11721      * Clear any invalid styles/messages for this field
11722      */
11723     clearInvalid : function()
11724     {
11725         
11726         if(!this.el || this.preventMark){ // not rendered
11727             return;
11728         }
11729         
11730         var label = this.el.select('label', true).first();
11731         var icon = this.el.select('i.fa-star', true).first();
11732         
11733         if(label && icon){
11734             icon.remove();
11735         }
11736         this.el.removeClass( this.validClass);
11737         this.inputEl().removeClass('is-invalid');
11738          
11739         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11740             
11741             var feedback = this.el.select('.form-control-feedback', true).first();
11742             
11743             if(feedback){
11744                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11745             }
11746             
11747         }
11748         
11749         this.fireEvent('valid', this);
11750     },
11751     
11752      /**
11753      * Mark this field as valid
11754      */
11755     markValid : function()
11756     {
11757         if(!this.el  || this.preventMark){ // not rendered
11758             return;
11759         }
11760         
11761         this.el.removeClass([this.invalidClass, this.validClass]);
11762         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11763         
11764         var feedback = this.el.select('.form-control-feedback', true).first();
11765             
11766         if(feedback){
11767             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11768         }
11769
11770         if(this.disabled || this.allowBlank){
11771             return;
11772         }
11773         
11774         var label = this.el.select('label', true).first();
11775         var icon = this.el.select('i.fa-star', true).first();
11776         
11777         if(label && icon){
11778             icon.remove();
11779         }
11780         if (Roo.bootstrap.version == 3) {
11781             this.el.addClass(this.validClass);
11782         } else {
11783             this.inputEl().addClass('is-valid');
11784         }
11785         
11786         
11787         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11788             
11789             var feedback = this.el.select('.form-control-feedback', true).first();
11790             
11791             if(feedback){
11792                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11793                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11794             }
11795             
11796         }
11797         
11798         this.fireEvent('valid', this);
11799     },
11800     
11801      /**
11802      * Mark this field as invalid
11803      * @param {String} msg The validation message
11804      */
11805     markInvalid : function(msg)
11806     {
11807         if(!this.el  || this.preventMark){ // not rendered
11808             return;
11809         }
11810         
11811         this.el.removeClass([this.invalidClass, this.validClass]);
11812         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11813         
11814         var feedback = this.el.select('.form-control-feedback', true).first();
11815             
11816         if(feedback){
11817             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11818         }
11819
11820         if(this.disabled || this.allowBlank){
11821             return;
11822         }
11823         
11824         var label = this.el.select('label', true).first();
11825         var icon = this.el.select('i.fa-star', true).first();
11826         
11827         if(!this.getValue().length && label && !icon){
11828             this.el.createChild({
11829                 tag : 'i',
11830                 cls : 'text-danger fa fa-lg fa-star',
11831                 tooltip : 'This field is required',
11832                 style : 'margin-right:5px;'
11833             }, label, true);
11834         }
11835         
11836         if (Roo.bootstrap.version == 3) {
11837             this.el.addClass(this.invalidClass);
11838         } else {
11839             this.inputEl().addClass('is-invalid');
11840         }
11841         
11842         // fixme ... this may be depricated need to test..
11843         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11844             
11845             var feedback = this.el.select('.form-control-feedback', true).first();
11846             
11847             if(feedback){
11848                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11849                 
11850                 if(this.getValue().length || this.forceFeedback){
11851                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11852                 }
11853                 
11854             }
11855             
11856         }
11857         
11858         this.fireEvent('invalid', this, msg);
11859     }
11860 });
11861
11862  
11863 /*
11864  * - LGPL
11865  *
11866  * trigger field - base class for combo..
11867  * 
11868  */
11869  
11870 /**
11871  * @class Roo.bootstrap.TriggerField
11872  * @extends Roo.bootstrap.Input
11873  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11874  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11875  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11876  * for which you can provide a custom implementation.  For example:
11877  * <pre><code>
11878 var trigger = new Roo.bootstrap.TriggerField();
11879 trigger.onTriggerClick = myTriggerFn;
11880 trigger.applyTo('my-field');
11881 </code></pre>
11882  *
11883  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11884  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11885  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
11886  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11887  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11888
11889  * @constructor
11890  * Create a new TriggerField.
11891  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11892  * to the base TextField)
11893  */
11894 Roo.bootstrap.TriggerField = function(config){
11895     this.mimicing = false;
11896     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11897 };
11898
11899 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
11900     /**
11901      * @cfg {String} triggerClass A CSS class to apply to the trigger
11902      */
11903      /**
11904      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11905      */
11906     hideTrigger:false,
11907
11908     /**
11909      * @cfg {Boolean} removable (true|false) special filter default false
11910      */
11911     removable : false,
11912     
11913     /** @cfg {Boolean} grow @hide */
11914     /** @cfg {Number} growMin @hide */
11915     /** @cfg {Number} growMax @hide */
11916
11917     /**
11918      * @hide 
11919      * @method
11920      */
11921     autoSize: Roo.emptyFn,
11922     // private
11923     monitorTab : true,
11924     // private
11925     deferHeight : true,
11926
11927     
11928     actionMode : 'wrap',
11929     
11930     caret : false,
11931     
11932     
11933     getAutoCreate : function(){
11934        
11935         var align = this.labelAlign || this.parentLabelAlign();
11936         
11937         var id = Roo.id();
11938         
11939         var cfg = {
11940             cls: 'form-group' //input-group
11941         };
11942         
11943         
11944         var input =  {
11945             tag: 'input',
11946             id : id,
11947             type : this.inputType,
11948             cls : 'form-control',
11949             autocomplete: 'new-password',
11950             placeholder : this.placeholder || '' 
11951             
11952         };
11953         if (this.name) {
11954             input.name = this.name;
11955         }
11956         if (this.size) {
11957             input.cls += ' input-' + this.size;
11958         }
11959         
11960         if (this.disabled) {
11961             input.disabled=true;
11962         }
11963         
11964         var inputblock = input;
11965         
11966         if(this.hasFeedback && !this.allowBlank){
11967             
11968             var feedback = {
11969                 tag: 'span',
11970                 cls: 'glyphicon form-control-feedback'
11971             };
11972             
11973             if(this.removable && !this.editable  ){
11974                 inputblock = {
11975                     cls : 'has-feedback',
11976                     cn :  [
11977                         inputblock,
11978                         {
11979                             tag: 'button',
11980                             html : 'x',
11981                             cls : 'roo-combo-removable-btn close'
11982                         },
11983                         feedback
11984                     ] 
11985                 };
11986             } else {
11987                 inputblock = {
11988                     cls : 'has-feedback',
11989                     cn :  [
11990                         inputblock,
11991                         feedback
11992                     ] 
11993                 };
11994             }
11995
11996         } else {
11997             if(this.removable && !this.editable ){
11998                 inputblock = {
11999                     cls : 'roo-removable',
12000                     cn :  [
12001                         inputblock,
12002                         {
12003                             tag: 'button',
12004                             html : 'x',
12005                             cls : 'roo-combo-removable-btn close'
12006                         }
12007                     ] 
12008                 };
12009             }
12010         }
12011         
12012         if (this.before || this.after) {
12013             
12014             inputblock = {
12015                 cls : 'input-group',
12016                 cn :  [] 
12017             };
12018             if (this.before) {
12019                 inputblock.cn.push({
12020                     tag :'span',
12021                     cls : 'input-group-addon input-group-prepend input-group-text',
12022                     html : this.before
12023                 });
12024             }
12025             
12026             inputblock.cn.push(input);
12027             
12028             if(this.hasFeedback && !this.allowBlank){
12029                 inputblock.cls += ' has-feedback';
12030                 inputblock.cn.push(feedback);
12031             }
12032             
12033             if (this.after) {
12034                 inputblock.cn.push({
12035                     tag :'span',
12036                     cls : 'input-group-addon input-group-append input-group-text',
12037                     html : this.after
12038                 });
12039             }
12040             
12041         };
12042         
12043       
12044         
12045         var ibwrap = inputblock;
12046         
12047         if(this.multiple){
12048             ibwrap = {
12049                 tag: 'ul',
12050                 cls: 'roo-select2-choices',
12051                 cn:[
12052                     {
12053                         tag: 'li',
12054                         cls: 'roo-select2-search-field',
12055                         cn: [
12056
12057                             inputblock
12058                         ]
12059                     }
12060                 ]
12061             };
12062                 
12063         }
12064         
12065         var combobox = {
12066             cls: 'roo-select2-container input-group',
12067             cn: [
12068                  {
12069                     tag: 'input',
12070                     type : 'hidden',
12071                     cls: 'form-hidden-field'
12072                 },
12073                 ibwrap
12074             ]
12075         };
12076         
12077         if(!this.multiple && this.showToggleBtn){
12078             
12079             var caret = {
12080                         tag: 'span',
12081                         cls: 'caret'
12082              };
12083             if (this.caret != false) {
12084                 caret = {
12085                      tag: 'i',
12086                      cls: 'fa fa-' + this.caret
12087                 };
12088                 
12089             }
12090             
12091             combobox.cn.push({
12092                 tag :'span',
12093                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12094                 cn : [
12095                     Roo.bootstrap.version == 3 ? caret : '',
12096                     {
12097                         tag: 'span',
12098                         cls: 'combobox-clear',
12099                         cn  : [
12100                             {
12101                                 tag : 'i',
12102                                 cls: 'icon-remove'
12103                             }
12104                         ]
12105                     }
12106                 ]
12107
12108             })
12109         }
12110         
12111         if(this.multiple){
12112             combobox.cls += ' roo-select2-container-multi';
12113         }
12114          var indicator = {
12115             tag : 'i',
12116             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12117             tooltip : 'This field is required'
12118         };
12119         if (Roo.bootstrap.version == 4) {
12120             indicator = {
12121                 tag : 'i',
12122                 style : 'display:none'
12123             };
12124         }
12125         
12126         
12127         if (align ==='left' && this.fieldLabel.length) {
12128             
12129             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12130
12131             cfg.cn = [
12132                 indicator,
12133                 {
12134                     tag: 'label',
12135                     'for' :  id,
12136                     cls : 'control-label',
12137                     html : this.fieldLabel
12138
12139                 },
12140                 {
12141                     cls : "", 
12142                     cn: [
12143                         combobox
12144                     ]
12145                 }
12146
12147             ];
12148             
12149             var labelCfg = cfg.cn[1];
12150             var contentCfg = cfg.cn[2];
12151             
12152             if(this.indicatorpos == 'right'){
12153                 cfg.cn = [
12154                     {
12155                         tag: 'label',
12156                         'for' :  id,
12157                         cls : 'control-label',
12158                         cn : [
12159                             {
12160                                 tag : 'span',
12161                                 html : this.fieldLabel
12162                             },
12163                             indicator
12164                         ]
12165                     },
12166                     {
12167                         cls : "", 
12168                         cn: [
12169                             combobox
12170                         ]
12171                     }
12172
12173                 ];
12174                 
12175                 labelCfg = cfg.cn[0];
12176                 contentCfg = cfg.cn[1];
12177             }
12178             
12179             if(this.labelWidth > 12){
12180                 labelCfg.style = "width: " + this.labelWidth + 'px';
12181             }
12182             
12183             if(this.labelWidth < 13 && this.labelmd == 0){
12184                 this.labelmd = this.labelWidth;
12185             }
12186             
12187             if(this.labellg > 0){
12188                 labelCfg.cls += ' col-lg-' + this.labellg;
12189                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12190             }
12191             
12192             if(this.labelmd > 0){
12193                 labelCfg.cls += ' col-md-' + this.labelmd;
12194                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12195             }
12196             
12197             if(this.labelsm > 0){
12198                 labelCfg.cls += ' col-sm-' + this.labelsm;
12199                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12200             }
12201             
12202             if(this.labelxs > 0){
12203                 labelCfg.cls += ' col-xs-' + this.labelxs;
12204                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12205             }
12206             
12207         } else if ( this.fieldLabel.length) {
12208 //                Roo.log(" label");
12209             cfg.cn = [
12210                 indicator,
12211                {
12212                    tag: 'label',
12213                    //cls : 'input-group-addon',
12214                    html : this.fieldLabel
12215
12216                },
12217
12218                combobox
12219
12220             ];
12221             
12222             if(this.indicatorpos == 'right'){
12223                 
12224                 cfg.cn = [
12225                     {
12226                        tag: 'label',
12227                        cn : [
12228                            {
12229                                tag : 'span',
12230                                html : this.fieldLabel
12231                            },
12232                            indicator
12233                        ]
12234
12235                     },
12236                     combobox
12237
12238                 ];
12239
12240             }
12241
12242         } else {
12243             
12244 //                Roo.log(" no label && no align");
12245                 cfg = combobox
12246                      
12247                 
12248         }
12249         
12250         var settings=this;
12251         ['xs','sm','md','lg'].map(function(size){
12252             if (settings[size]) {
12253                 cfg.cls += ' col-' + size + '-' + settings[size];
12254             }
12255         });
12256         
12257         return cfg;
12258         
12259     },
12260     
12261     
12262     
12263     // private
12264     onResize : function(w, h){
12265 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12266 //        if(typeof w == 'number'){
12267 //            var x = w - this.trigger.getWidth();
12268 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12269 //            this.trigger.setStyle('left', x+'px');
12270 //        }
12271     },
12272
12273     // private
12274     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12275
12276     // private
12277     getResizeEl : function(){
12278         return this.inputEl();
12279     },
12280
12281     // private
12282     getPositionEl : function(){
12283         return this.inputEl();
12284     },
12285
12286     // private
12287     alignErrorIcon : function(){
12288         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12289     },
12290
12291     // private
12292     initEvents : function(){
12293         
12294         this.createList();
12295         
12296         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12297         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12298         if(!this.multiple && this.showToggleBtn){
12299             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12300             if(this.hideTrigger){
12301                 this.trigger.setDisplayed(false);
12302             }
12303             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12304         }
12305         
12306         if(this.multiple){
12307             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12308         }
12309         
12310         if(this.removable && !this.editable && !this.tickable){
12311             var close = this.closeTriggerEl();
12312             
12313             if(close){
12314                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12315                 close.on('click', this.removeBtnClick, this, close);
12316             }
12317         }
12318         
12319         //this.trigger.addClassOnOver('x-form-trigger-over');
12320         //this.trigger.addClassOnClick('x-form-trigger-click');
12321         
12322         //if(!this.width){
12323         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12324         //}
12325     },
12326     
12327     closeTriggerEl : function()
12328     {
12329         var close = this.el.select('.roo-combo-removable-btn', true).first();
12330         return close ? close : false;
12331     },
12332     
12333     removeBtnClick : function(e, h, el)
12334     {
12335         e.preventDefault();
12336         
12337         if(this.fireEvent("remove", this) !== false){
12338             this.reset();
12339             this.fireEvent("afterremove", this)
12340         }
12341     },
12342     
12343     createList : function()
12344     {
12345         this.list = Roo.get(document.body).createChild({
12346             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12347             cls: 'typeahead typeahead-long dropdown-menu shadow',
12348             style: 'display:none'
12349         });
12350         
12351         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12352         
12353     },
12354
12355     // private
12356     initTrigger : function(){
12357        
12358     },
12359
12360     // private
12361     onDestroy : function(){
12362         if(this.trigger){
12363             this.trigger.removeAllListeners();
12364           //  this.trigger.remove();
12365         }
12366         //if(this.wrap){
12367         //    this.wrap.remove();
12368         //}
12369         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12370     },
12371
12372     // private
12373     onFocus : function(){
12374         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12375         /*
12376         if(!this.mimicing){
12377             this.wrap.addClass('x-trigger-wrap-focus');
12378             this.mimicing = true;
12379             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12380             if(this.monitorTab){
12381                 this.el.on("keydown", this.checkTab, this);
12382             }
12383         }
12384         */
12385     },
12386
12387     // private
12388     checkTab : function(e){
12389         if(e.getKey() == e.TAB){
12390             this.triggerBlur();
12391         }
12392     },
12393
12394     // private
12395     onBlur : function(){
12396         // do nothing
12397     },
12398
12399     // private
12400     mimicBlur : function(e, t){
12401         /*
12402         if(!this.wrap.contains(t) && this.validateBlur()){
12403             this.triggerBlur();
12404         }
12405         */
12406     },
12407
12408     // private
12409     triggerBlur : function(){
12410         this.mimicing = false;
12411         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12412         if(this.monitorTab){
12413             this.el.un("keydown", this.checkTab, this);
12414         }
12415         //this.wrap.removeClass('x-trigger-wrap-focus');
12416         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12417     },
12418
12419     // private
12420     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12421     validateBlur : function(e, t){
12422         return true;
12423     },
12424
12425     // private
12426     onDisable : function(){
12427         this.inputEl().dom.disabled = true;
12428         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12429         //if(this.wrap){
12430         //    this.wrap.addClass('x-item-disabled');
12431         //}
12432     },
12433
12434     // private
12435     onEnable : function(){
12436         this.inputEl().dom.disabled = false;
12437         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12438         //if(this.wrap){
12439         //    this.el.removeClass('x-item-disabled');
12440         //}
12441     },
12442
12443     // private
12444     onShow : function(){
12445         var ae = this.getActionEl();
12446         
12447         if(ae){
12448             ae.dom.style.display = '';
12449             ae.dom.style.visibility = 'visible';
12450         }
12451     },
12452
12453     // private
12454     
12455     onHide : function(){
12456         var ae = this.getActionEl();
12457         ae.dom.style.display = 'none';
12458     },
12459
12460     /**
12461      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12462      * by an implementing function.
12463      * @method
12464      * @param {EventObject} e
12465      */
12466     onTriggerClick : Roo.emptyFn
12467 });
12468  
12469 /*
12470 * Licence: LGPL
12471 */
12472
12473 /**
12474  * @class Roo.bootstrap.CardUploader
12475  * @extends Roo.bootstrap.Button
12476  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12477  * @cfg {Number} errorTimeout default 3000
12478  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12479  * @cfg {Array}  html The button text.
12480
12481  *
12482  * @constructor
12483  * Create a new CardUploader
12484  * @param {Object} config The config object
12485  */
12486
12487 Roo.bootstrap.CardUploader = function(config){
12488     
12489  
12490     
12491     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12492     
12493     
12494     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12495         return r.data.id
12496         });
12497     
12498     
12499 };
12500
12501 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12502     
12503      
12504     errorTimeout : 3000,
12505      
12506     images : false,
12507    
12508     fileCollection : false,
12509     allowBlank : true,
12510     
12511     getAutoCreate : function()
12512     {
12513         
12514         var cfg =  {
12515             cls :'form-group' ,
12516             cn : [
12517                
12518                 {
12519                     tag: 'label',
12520                    //cls : 'input-group-addon',
12521                     html : this.fieldLabel
12522
12523                 },
12524
12525                 {
12526                     tag: 'input',
12527                     type : 'hidden',
12528                     value : this.value,
12529                     cls : 'd-none  form-control'
12530                 },
12531                 
12532                 {
12533                     tag: 'input',
12534                     multiple : 'multiple',
12535                     type : 'file',
12536                     cls : 'd-none  roo-card-upload-selector'
12537                 },
12538                 
12539                 {
12540                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12541                 },
12542                 {
12543                     cls : 'card-columns roo-card-uploader-container'
12544                 }
12545
12546             ]
12547         };
12548            
12549          
12550         return cfg;
12551     },
12552     
12553     getChildContainer : function() /// what children are added to.
12554     {
12555         return this.containerEl;
12556     },
12557    
12558     getButtonContainer : function() /// what children are added to.
12559     {
12560         return this.el.select(".roo-card-uploader-button-container").first();
12561     },
12562    
12563     initEvents : function()
12564     {
12565         
12566         Roo.bootstrap.Input.prototype.initEvents.call(this);
12567         
12568         var t = this;
12569         this.addxtype({
12570             xns: Roo.bootstrap,
12571
12572             xtype : 'Button',
12573             container_method : 'getButtonContainer' ,            
12574             html :  this.html, // fix changable?
12575             cls : 'w-100 ',
12576             listeners : {
12577                 'click' : function(btn, e) {
12578                     t.onClick(e);
12579                 }
12580             }
12581         });
12582         
12583         
12584         
12585         
12586         this.urlAPI = (window.createObjectURL && window) || 
12587                                 (window.URL && URL.revokeObjectURL && URL) || 
12588                                 (window.webkitURL && webkitURL);
12589                         
12590          
12591          
12592          
12593         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12594         
12595         this.selectorEl.on('change', this.onFileSelected, this);
12596         if (this.images) {
12597             var t = this;
12598             this.images.forEach(function(img) {
12599                 t.addCard(img)
12600             });
12601             this.images = false;
12602         }
12603         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12604          
12605        
12606     },
12607     
12608    
12609     onClick : function(e)
12610     {
12611         e.preventDefault();
12612          
12613         this.selectorEl.dom.click();
12614          
12615     },
12616     
12617     onFileSelected : function(e)
12618     {
12619         e.preventDefault();
12620         
12621         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12622             return;
12623         }
12624         
12625         Roo.each(this.selectorEl.dom.files, function(file){    
12626             this.addFile(file);
12627         }, this);
12628          
12629     },
12630     
12631       
12632     
12633       
12634     
12635     addFile : function(file)
12636     {
12637            
12638         if(typeof(file) === 'string'){
12639             throw "Add file by name?"; // should not happen
12640             return;
12641         }
12642         
12643         if(!file || !this.urlAPI){
12644             return;
12645         }
12646         
12647         // file;
12648         // file.type;
12649         
12650         var _this = this;
12651         
12652         
12653         var url = _this.urlAPI.createObjectURL( file);
12654            
12655         this.addCard({
12656             id : Roo.bootstrap.CardUploader.ID--,
12657             is_uploaded : false,
12658             src : url,
12659             title : file.name,
12660             mimetype : file.type,
12661             preview : false,
12662             is_deleted : 0
12663         })
12664         
12665     },
12666     
12667     addCard : function (data)
12668     {
12669         // hidden input element?
12670         // if the file is not an image...
12671         //then we need to use something other that and header_image
12672         var t = this;
12673         //   remove.....
12674         var footer = [
12675             {
12676                 xns : Roo.bootstrap,
12677                 xtype : 'CardFooter',
12678                 items: [
12679                     {
12680                         xns : Roo.bootstrap,
12681                         xtype : 'Element',
12682                         cls : 'd-flex',
12683                         items : [
12684                             
12685                             {
12686                                 xns : Roo.bootstrap,
12687                                 xtype : 'Button',
12688                                 html : String.format("<small>{0}</small>", data.title),
12689                                 cls : 'col-11 text-left',
12690                                 size: 'sm',
12691                                 weight: 'link',
12692                                 fa : 'download',
12693                                 listeners : {
12694                                     click : function() {
12695                                         this.downloadCard(data.id)
12696                                     }
12697                                 }
12698                             },
12699                           
12700                             {
12701                                 xns : Roo.bootstrap,
12702                                 xtype : 'Button',
12703                                 
12704                                 size : 'sm',
12705                                 weight: 'danger',
12706                                 cls : 'col-1',
12707                                 fa : 'times',
12708                                 listeners : {
12709                                     click : function() {
12710                                         t.removeCard(data.id)
12711                                     }
12712                                 }
12713                             }
12714                         ]
12715                     }
12716                     
12717                 ] 
12718             }
12719             
12720         ];
12721
12722         var cn = this.addxtype(
12723             {
12724                  
12725                 xns : Roo.bootstrap,
12726                 xtype : 'Card',
12727                 closeable : true,
12728                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12729                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
12730                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
12731                 data : data,
12732                 html : false,
12733                  
12734                 items : footer,
12735                 initEvents : function() {
12736                     Roo.bootstrap.Card.prototype.initEvents.call(this);
12737                     this.imgEl = this.el.select('.card-img-top').first();
12738                     if (this.imgEl) {
12739                         this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12740                         this.imgEl.set({ 'pointer' : 'cursor' });
12741                                   
12742                     }
12743                     
12744                   
12745                 }
12746                 
12747             }
12748         );
12749         // dont' really need ot update items.
12750         // this.items.push(cn);
12751         this.fileCollection.add(cn);
12752         this.updateInput();
12753         
12754     },
12755     removeCard : function(id)
12756     {
12757         
12758         var card  = this.fileCollection.get(id);
12759         card.data.is_deleted = 1;
12760         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12761         this.fileCollection.remove(card);
12762         //this.items = this.items.filter(function(e) { return e != card });
12763         // dont' really need ot update items.
12764         card.el.dom.parentNode.removeChild(card.el.dom);
12765         
12766     },
12767     reset: function()
12768     {
12769         this.fileCollection.each(function(card) {
12770             card.el.dom.parentNode.removeChild(card.el.dom);    
12771         });
12772         this.fileCollection.clear();
12773         this.updateInput();
12774     },
12775     
12776     updateInput : function()
12777     {
12778         var data = [];
12779         this.fileCollection.each(function(e) {
12780             data.push(e.data);
12781         });
12782         
12783         this.inputEl().dom.value = JSON.stringify(data);
12784     }
12785     
12786     
12787 });
12788
12789
12790 Roo.bootstrap.CardUploader.ID = -1;/*
12791  * Based on:
12792  * Ext JS Library 1.1.1
12793  * Copyright(c) 2006-2007, Ext JS, LLC.
12794  *
12795  * Originally Released Under LGPL - original licence link has changed is not relivant.
12796  *
12797  * Fork - LGPL
12798  * <script type="text/javascript">
12799  */
12800
12801
12802 /**
12803  * @class Roo.data.SortTypes
12804  * @singleton
12805  * Defines the default sorting (casting?) comparison functions used when sorting data.
12806  */
12807 Roo.data.SortTypes = {
12808     /**
12809      * Default sort that does nothing
12810      * @param {Mixed} s The value being converted
12811      * @return {Mixed} The comparison value
12812      */
12813     none : function(s){
12814         return s;
12815     },
12816     
12817     /**
12818      * The regular expression used to strip tags
12819      * @type {RegExp}
12820      * @property
12821      */
12822     stripTagsRE : /<\/?[^>]+>/gi,
12823     
12824     /**
12825      * Strips all HTML tags to sort on text only
12826      * @param {Mixed} s The value being converted
12827      * @return {String} The comparison value
12828      */
12829     asText : function(s){
12830         return String(s).replace(this.stripTagsRE, "");
12831     },
12832     
12833     /**
12834      * Strips all HTML tags to sort on text only - Case insensitive
12835      * @param {Mixed} s The value being converted
12836      * @return {String} The comparison value
12837      */
12838     asUCText : function(s){
12839         return String(s).toUpperCase().replace(this.stripTagsRE, "");
12840     },
12841     
12842     /**
12843      * Case insensitive string
12844      * @param {Mixed} s The value being converted
12845      * @return {String} The comparison value
12846      */
12847     asUCString : function(s) {
12848         return String(s).toUpperCase();
12849     },
12850     
12851     /**
12852      * Date sorting
12853      * @param {Mixed} s The value being converted
12854      * @return {Number} The comparison value
12855      */
12856     asDate : function(s) {
12857         if(!s){
12858             return 0;
12859         }
12860         if(s instanceof Date){
12861             return s.getTime();
12862         }
12863         return Date.parse(String(s));
12864     },
12865     
12866     /**
12867      * Float sorting
12868      * @param {Mixed} s The value being converted
12869      * @return {Float} The comparison value
12870      */
12871     asFloat : function(s) {
12872         var val = parseFloat(String(s).replace(/,/g, ""));
12873         if(isNaN(val)) {
12874             val = 0;
12875         }
12876         return val;
12877     },
12878     
12879     /**
12880      * Integer sorting
12881      * @param {Mixed} s The value being converted
12882      * @return {Number} The comparison value
12883      */
12884     asInt : function(s) {
12885         var val = parseInt(String(s).replace(/,/g, ""));
12886         if(isNaN(val)) {
12887             val = 0;
12888         }
12889         return val;
12890     }
12891 };/*
12892  * Based on:
12893  * Ext JS Library 1.1.1
12894  * Copyright(c) 2006-2007, Ext JS, LLC.
12895  *
12896  * Originally Released Under LGPL - original licence link has changed is not relivant.
12897  *
12898  * Fork - LGPL
12899  * <script type="text/javascript">
12900  */
12901
12902 /**
12903 * @class Roo.data.Record
12904  * Instances of this class encapsulate both record <em>definition</em> information, and record
12905  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12906  * to access Records cached in an {@link Roo.data.Store} object.<br>
12907  * <p>
12908  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12909  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12910  * objects.<br>
12911  * <p>
12912  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12913  * @constructor
12914  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12915  * {@link #create}. The parameters are the same.
12916  * @param {Array} data An associative Array of data values keyed by the field name.
12917  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12918  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12919  * not specified an integer id is generated.
12920  */
12921 Roo.data.Record = function(data, id){
12922     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12923     this.data = data;
12924 };
12925
12926 /**
12927  * Generate a constructor for a specific record layout.
12928  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12929  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12930  * Each field definition object may contain the following properties: <ul>
12931  * <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,
12932  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12933  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12934  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12935  * is being used, then this is a string containing the javascript expression to reference the data relative to 
12936  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12937  * to the data item relative to the record element. If the mapping expression is the same as the field name,
12938  * this may be omitted.</p></li>
12939  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12940  * <ul><li>auto (Default, implies no conversion)</li>
12941  * <li>string</li>
12942  * <li>int</li>
12943  * <li>float</li>
12944  * <li>boolean</li>
12945  * <li>date</li></ul></p></li>
12946  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12947  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12948  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12949  * by the Reader into an object that will be stored in the Record. It is passed the
12950  * following parameters:<ul>
12951  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12952  * </ul></p></li>
12953  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12954  * </ul>
12955  * <br>usage:<br><pre><code>
12956 var TopicRecord = Roo.data.Record.create(
12957     {name: 'title', mapping: 'topic_title'},
12958     {name: 'author', mapping: 'username'},
12959     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12960     {name: 'lastPost', mapping: 'post_time', type: 'date'},
12961     {name: 'lastPoster', mapping: 'user2'},
12962     {name: 'excerpt', mapping: 'post_text'}
12963 );
12964
12965 var myNewRecord = new TopicRecord({
12966     title: 'Do my job please',
12967     author: 'noobie',
12968     totalPosts: 1,
12969     lastPost: new Date(),
12970     lastPoster: 'Animal',
12971     excerpt: 'No way dude!'
12972 });
12973 myStore.add(myNewRecord);
12974 </code></pre>
12975  * @method create
12976  * @static
12977  */
12978 Roo.data.Record.create = function(o){
12979     var f = function(){
12980         f.superclass.constructor.apply(this, arguments);
12981     };
12982     Roo.extend(f, Roo.data.Record);
12983     var p = f.prototype;
12984     p.fields = new Roo.util.MixedCollection(false, function(field){
12985         return field.name;
12986     });
12987     for(var i = 0, len = o.length; i < len; i++){
12988         p.fields.add(new Roo.data.Field(o[i]));
12989     }
12990     f.getField = function(name){
12991         return p.fields.get(name);  
12992     };
12993     return f;
12994 };
12995
12996 Roo.data.Record.AUTO_ID = 1000;
12997 Roo.data.Record.EDIT = 'edit';
12998 Roo.data.Record.REJECT = 'reject';
12999 Roo.data.Record.COMMIT = 'commit';
13000
13001 Roo.data.Record.prototype = {
13002     /**
13003      * Readonly flag - true if this record has been modified.
13004      * @type Boolean
13005      */
13006     dirty : false,
13007     editing : false,
13008     error: null,
13009     modified: null,
13010
13011     // private
13012     join : function(store){
13013         this.store = store;
13014     },
13015
13016     /**
13017      * Set the named field to the specified value.
13018      * @param {String} name The name of the field to set.
13019      * @param {Object} value The value to set the field to.
13020      */
13021     set : function(name, value){
13022         if(this.data[name] == value){
13023             return;
13024         }
13025         this.dirty = true;
13026         if(!this.modified){
13027             this.modified = {};
13028         }
13029         if(typeof this.modified[name] == 'undefined'){
13030             this.modified[name] = this.data[name];
13031         }
13032         this.data[name] = value;
13033         if(!this.editing && this.store){
13034             this.store.afterEdit(this);
13035         }       
13036     },
13037
13038     /**
13039      * Get the value of the named field.
13040      * @param {String} name The name of the field to get the value of.
13041      * @return {Object} The value of the field.
13042      */
13043     get : function(name){
13044         return this.data[name]; 
13045     },
13046
13047     // private
13048     beginEdit : function(){
13049         this.editing = true;
13050         this.modified = {}; 
13051     },
13052
13053     // private
13054     cancelEdit : function(){
13055         this.editing = false;
13056         delete this.modified;
13057     },
13058
13059     // private
13060     endEdit : function(){
13061         this.editing = false;
13062         if(this.dirty && this.store){
13063             this.store.afterEdit(this);
13064         }
13065     },
13066
13067     /**
13068      * Usually called by the {@link Roo.data.Store} which owns the Record.
13069      * Rejects all changes made to the Record since either creation, or the last commit operation.
13070      * Modified fields are reverted to their original values.
13071      * <p>
13072      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13073      * of reject operations.
13074      */
13075     reject : function(){
13076         var m = this.modified;
13077         for(var n in m){
13078             if(typeof m[n] != "function"){
13079                 this.data[n] = m[n];
13080             }
13081         }
13082         this.dirty = false;
13083         delete this.modified;
13084         this.editing = false;
13085         if(this.store){
13086             this.store.afterReject(this);
13087         }
13088     },
13089
13090     /**
13091      * Usually called by the {@link Roo.data.Store} which owns the Record.
13092      * Commits all changes made to the Record since either creation, or the last commit operation.
13093      * <p>
13094      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13095      * of commit operations.
13096      */
13097     commit : function(){
13098         this.dirty = false;
13099         delete this.modified;
13100         this.editing = false;
13101         if(this.store){
13102             this.store.afterCommit(this);
13103         }
13104     },
13105
13106     // private
13107     hasError : function(){
13108         return this.error != null;
13109     },
13110
13111     // private
13112     clearError : function(){
13113         this.error = null;
13114     },
13115
13116     /**
13117      * Creates a copy of this record.
13118      * @param {String} id (optional) A new record id if you don't want to use this record's id
13119      * @return {Record}
13120      */
13121     copy : function(newId) {
13122         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13123     }
13124 };/*
13125  * Based on:
13126  * Ext JS Library 1.1.1
13127  * Copyright(c) 2006-2007, Ext JS, LLC.
13128  *
13129  * Originally Released Under LGPL - original licence link has changed is not relivant.
13130  *
13131  * Fork - LGPL
13132  * <script type="text/javascript">
13133  */
13134
13135
13136
13137 /**
13138  * @class Roo.data.Store
13139  * @extends Roo.util.Observable
13140  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13141  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13142  * <p>
13143  * 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
13144  * has no knowledge of the format of the data returned by the Proxy.<br>
13145  * <p>
13146  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13147  * instances from the data object. These records are cached and made available through accessor functions.
13148  * @constructor
13149  * Creates a new Store.
13150  * @param {Object} config A config object containing the objects needed for the Store to access data,
13151  * and read the data into Records.
13152  */
13153 Roo.data.Store = function(config){
13154     this.data = new Roo.util.MixedCollection(false);
13155     this.data.getKey = function(o){
13156         return o.id;
13157     };
13158     this.baseParams = {};
13159     // private
13160     this.paramNames = {
13161         "start" : "start",
13162         "limit" : "limit",
13163         "sort" : "sort",
13164         "dir" : "dir",
13165         "multisort" : "_multisort"
13166     };
13167
13168     if(config && config.data){
13169         this.inlineData = config.data;
13170         delete config.data;
13171     }
13172
13173     Roo.apply(this, config);
13174     
13175     if(this.reader){ // reader passed
13176         this.reader = Roo.factory(this.reader, Roo.data);
13177         this.reader.xmodule = this.xmodule || false;
13178         if(!this.recordType){
13179             this.recordType = this.reader.recordType;
13180         }
13181         if(this.reader.onMetaChange){
13182             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13183         }
13184     }
13185
13186     if(this.recordType){
13187         this.fields = this.recordType.prototype.fields;
13188     }
13189     this.modified = [];
13190
13191     this.addEvents({
13192         /**
13193          * @event datachanged
13194          * Fires when the data cache has changed, and a widget which is using this Store
13195          * as a Record cache should refresh its view.
13196          * @param {Store} this
13197          */
13198         datachanged : true,
13199         /**
13200          * @event metachange
13201          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13202          * @param {Store} this
13203          * @param {Object} meta The JSON metadata
13204          */
13205         metachange : true,
13206         /**
13207          * @event add
13208          * Fires when Records have been added to the Store
13209          * @param {Store} this
13210          * @param {Roo.data.Record[]} records The array of Records added
13211          * @param {Number} index The index at which the record(s) were added
13212          */
13213         add : true,
13214         /**
13215          * @event remove
13216          * Fires when a Record has been removed from the Store
13217          * @param {Store} this
13218          * @param {Roo.data.Record} record The Record that was removed
13219          * @param {Number} index The index at which the record was removed
13220          */
13221         remove : true,
13222         /**
13223          * @event update
13224          * Fires when a Record has been updated
13225          * @param {Store} this
13226          * @param {Roo.data.Record} record The Record that was updated
13227          * @param {String} operation The update operation being performed.  Value may be one of:
13228          * <pre><code>
13229  Roo.data.Record.EDIT
13230  Roo.data.Record.REJECT
13231  Roo.data.Record.COMMIT
13232          * </code></pre>
13233          */
13234         update : true,
13235         /**
13236          * @event clear
13237          * Fires when the data cache has been cleared.
13238          * @param {Store} this
13239          */
13240         clear : true,
13241         /**
13242          * @event beforeload
13243          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13244          * the load action will be canceled.
13245          * @param {Store} this
13246          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13247          */
13248         beforeload : true,
13249         /**
13250          * @event beforeloadadd
13251          * Fires after a new set of Records has been loaded.
13252          * @param {Store} this
13253          * @param {Roo.data.Record[]} records The Records that were loaded
13254          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13255          */
13256         beforeloadadd : true,
13257         /**
13258          * @event load
13259          * Fires after a new set of Records has been loaded, before they are added to the store.
13260          * @param {Store} this
13261          * @param {Roo.data.Record[]} records The Records that were loaded
13262          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13263          * @params {Object} return from reader
13264          */
13265         load : true,
13266         /**
13267          * @event loadexception
13268          * Fires if an exception occurs in the Proxy during loading.
13269          * Called with the signature of the Proxy's "loadexception" event.
13270          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13271          * 
13272          * @param {Proxy} 
13273          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13274          * @param {Object} load options 
13275          * @param {Object} jsonData from your request (normally this contains the Exception)
13276          */
13277         loadexception : true
13278     });
13279     
13280     if(this.proxy){
13281         this.proxy = Roo.factory(this.proxy, Roo.data);
13282         this.proxy.xmodule = this.xmodule || false;
13283         this.relayEvents(this.proxy,  ["loadexception"]);
13284     }
13285     this.sortToggle = {};
13286     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13287
13288     Roo.data.Store.superclass.constructor.call(this);
13289
13290     if(this.inlineData){
13291         this.loadData(this.inlineData);
13292         delete this.inlineData;
13293     }
13294 };
13295
13296 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13297      /**
13298     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13299     * without a remote query - used by combo/forms at present.
13300     */
13301     
13302     /**
13303     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13304     */
13305     /**
13306     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13307     */
13308     /**
13309     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13310     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13311     */
13312     /**
13313     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13314     * on any HTTP request
13315     */
13316     /**
13317     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13318     */
13319     /**
13320     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13321     */
13322     multiSort: false,
13323     /**
13324     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13325     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13326     */
13327     remoteSort : false,
13328
13329     /**
13330     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13331      * loaded or when a record is removed. (defaults to false).
13332     */
13333     pruneModifiedRecords : false,
13334
13335     // private
13336     lastOptions : null,
13337
13338     /**
13339      * Add Records to the Store and fires the add event.
13340      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13341      */
13342     add : function(records){
13343         records = [].concat(records);
13344         for(var i = 0, len = records.length; i < len; i++){
13345             records[i].join(this);
13346         }
13347         var index = this.data.length;
13348         this.data.addAll(records);
13349         this.fireEvent("add", this, records, index);
13350     },
13351
13352     /**
13353      * Remove a Record from the Store and fires the remove event.
13354      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13355      */
13356     remove : function(record){
13357         var index = this.data.indexOf(record);
13358         this.data.removeAt(index);
13359  
13360         if(this.pruneModifiedRecords){
13361             this.modified.remove(record);
13362         }
13363         this.fireEvent("remove", this, record, index);
13364     },
13365
13366     /**
13367      * Remove all Records from the Store and fires the clear event.
13368      */
13369     removeAll : function(){
13370         this.data.clear();
13371         if(this.pruneModifiedRecords){
13372             this.modified = [];
13373         }
13374         this.fireEvent("clear", this);
13375     },
13376
13377     /**
13378      * Inserts Records to the Store at the given index and fires the add event.
13379      * @param {Number} index The start index at which to insert the passed Records.
13380      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13381      */
13382     insert : function(index, records){
13383         records = [].concat(records);
13384         for(var i = 0, len = records.length; i < len; i++){
13385             this.data.insert(index, records[i]);
13386             records[i].join(this);
13387         }
13388         this.fireEvent("add", this, records, index);
13389     },
13390
13391     /**
13392      * Get the index within the cache of the passed Record.
13393      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13394      * @return {Number} The index of the passed Record. Returns -1 if not found.
13395      */
13396     indexOf : function(record){
13397         return this.data.indexOf(record);
13398     },
13399
13400     /**
13401      * Get the index within the cache of the Record with the passed id.
13402      * @param {String} id The id of the Record to find.
13403      * @return {Number} The index of the Record. Returns -1 if not found.
13404      */
13405     indexOfId : function(id){
13406         return this.data.indexOfKey(id);
13407     },
13408
13409     /**
13410      * Get the Record with the specified id.
13411      * @param {String} id The id of the Record to find.
13412      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13413      */
13414     getById : function(id){
13415         return this.data.key(id);
13416     },
13417
13418     /**
13419      * Get the Record at the specified index.
13420      * @param {Number} index The index of the Record to find.
13421      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13422      */
13423     getAt : function(index){
13424         return this.data.itemAt(index);
13425     },
13426
13427     /**
13428      * Returns a range of Records between specified indices.
13429      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13430      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13431      * @return {Roo.data.Record[]} An array of Records
13432      */
13433     getRange : function(start, end){
13434         return this.data.getRange(start, end);
13435     },
13436
13437     // private
13438     storeOptions : function(o){
13439         o = Roo.apply({}, o);
13440         delete o.callback;
13441         delete o.scope;
13442         this.lastOptions = o;
13443     },
13444
13445     /**
13446      * Loads the Record cache from the configured Proxy using the configured Reader.
13447      * <p>
13448      * If using remote paging, then the first load call must specify the <em>start</em>
13449      * and <em>limit</em> properties in the options.params property to establish the initial
13450      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13451      * <p>
13452      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13453      * and this call will return before the new data has been loaded. Perform any post-processing
13454      * in a callback function, or in a "load" event handler.</strong>
13455      * <p>
13456      * @param {Object} options An object containing properties which control loading options:<ul>
13457      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13458      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13459      * passed the following arguments:<ul>
13460      * <li>r : Roo.data.Record[]</li>
13461      * <li>options: Options object from the load call</li>
13462      * <li>success: Boolean success indicator</li></ul></li>
13463      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13464      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13465      * </ul>
13466      */
13467     load : function(options){
13468         options = options || {};
13469         if(this.fireEvent("beforeload", this, options) !== false){
13470             this.storeOptions(options);
13471             var p = Roo.apply(options.params || {}, this.baseParams);
13472             // if meta was not loaded from remote source.. try requesting it.
13473             if (!this.reader.metaFromRemote) {
13474                 p._requestMeta = 1;
13475             }
13476             if(this.sortInfo && this.remoteSort){
13477                 var pn = this.paramNames;
13478                 p[pn["sort"]] = this.sortInfo.field;
13479                 p[pn["dir"]] = this.sortInfo.direction;
13480             }
13481             if (this.multiSort) {
13482                 var pn = this.paramNames;
13483                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13484             }
13485             
13486             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13487         }
13488     },
13489
13490     /**
13491      * Reloads the Record cache from the configured Proxy using the configured Reader and
13492      * the options from the last load operation performed.
13493      * @param {Object} options (optional) An object containing properties which may override the options
13494      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13495      * the most recently used options are reused).
13496      */
13497     reload : function(options){
13498         this.load(Roo.applyIf(options||{}, this.lastOptions));
13499     },
13500
13501     // private
13502     // Called as a callback by the Reader during a load operation.
13503     loadRecords : function(o, options, success){
13504         if(!o || success === false){
13505             if(success !== false){
13506                 this.fireEvent("load", this, [], options, o);
13507             }
13508             if(options.callback){
13509                 options.callback.call(options.scope || this, [], options, false);
13510             }
13511             return;
13512         }
13513         // if data returned failure - throw an exception.
13514         if (o.success === false) {
13515             // show a message if no listener is registered.
13516             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13517                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13518             }
13519             // loadmask wil be hooked into this..
13520             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13521             return;
13522         }
13523         var r = o.records, t = o.totalRecords || r.length;
13524         
13525         this.fireEvent("beforeloadadd", this, r, options, o);
13526         
13527         if(!options || options.add !== true){
13528             if(this.pruneModifiedRecords){
13529                 this.modified = [];
13530             }
13531             for(var i = 0, len = r.length; i < len; i++){
13532                 r[i].join(this);
13533             }
13534             if(this.snapshot){
13535                 this.data = this.snapshot;
13536                 delete this.snapshot;
13537             }
13538             this.data.clear();
13539             this.data.addAll(r);
13540             this.totalLength = t;
13541             this.applySort();
13542             this.fireEvent("datachanged", this);
13543         }else{
13544             this.totalLength = Math.max(t, this.data.length+r.length);
13545             this.add(r);
13546         }
13547         
13548         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13549                 
13550             var e = new Roo.data.Record({});
13551
13552             e.set(this.parent.displayField, this.parent.emptyTitle);
13553             e.set(this.parent.valueField, '');
13554
13555             this.insert(0, e);
13556         }
13557             
13558         this.fireEvent("load", this, r, options, o);
13559         if(options.callback){
13560             options.callback.call(options.scope || this, r, options, true);
13561         }
13562     },
13563
13564
13565     /**
13566      * Loads data from a passed data block. A Reader which understands the format of the data
13567      * must have been configured in the constructor.
13568      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13569      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13570      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13571      */
13572     loadData : function(o, append){
13573         var r = this.reader.readRecords(o);
13574         this.loadRecords(r, {add: append}, true);
13575     },
13576     
13577      /**
13578      * using 'cn' the nested child reader read the child array into it's child stores.
13579      * @param {Object} rec The record with a 'children array
13580      */
13581     loadDataFromChildren : function(rec)
13582     {
13583         this.loadData(this.reader.toLoadData(rec));
13584     },
13585     
13586
13587     /**
13588      * Gets the number of cached records.
13589      * <p>
13590      * <em>If using paging, this may not be the total size of the dataset. If the data object
13591      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13592      * the data set size</em>
13593      */
13594     getCount : function(){
13595         return this.data.length || 0;
13596     },
13597
13598     /**
13599      * Gets the total number of records in the dataset as returned by the server.
13600      * <p>
13601      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13602      * the dataset size</em>
13603      */
13604     getTotalCount : function(){
13605         return this.totalLength || 0;
13606     },
13607
13608     /**
13609      * Returns the sort state of the Store as an object with two properties:
13610      * <pre><code>
13611  field {String} The name of the field by which the Records are sorted
13612  direction {String} The sort order, "ASC" or "DESC"
13613      * </code></pre>
13614      */
13615     getSortState : function(){
13616         return this.sortInfo;
13617     },
13618
13619     // private
13620     applySort : function(){
13621         if(this.sortInfo && !this.remoteSort){
13622             var s = this.sortInfo, f = s.field;
13623             var st = this.fields.get(f).sortType;
13624             var fn = function(r1, r2){
13625                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13626                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13627             };
13628             this.data.sort(s.direction, fn);
13629             if(this.snapshot && this.snapshot != this.data){
13630                 this.snapshot.sort(s.direction, fn);
13631             }
13632         }
13633     },
13634
13635     /**
13636      * Sets the default sort column and order to be used by the next load operation.
13637      * @param {String} fieldName The name of the field to sort by.
13638      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13639      */
13640     setDefaultSort : function(field, dir){
13641         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13642     },
13643
13644     /**
13645      * Sort the Records.
13646      * If remote sorting is used, the sort is performed on the server, and the cache is
13647      * reloaded. If local sorting is used, the cache is sorted internally.
13648      * @param {String} fieldName The name of the field to sort by.
13649      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13650      */
13651     sort : function(fieldName, dir){
13652         var f = this.fields.get(fieldName);
13653         if(!dir){
13654             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13655             
13656             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13657                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13658             }else{
13659                 dir = f.sortDir;
13660             }
13661         }
13662         this.sortToggle[f.name] = dir;
13663         this.sortInfo = {field: f.name, direction: dir};
13664         if(!this.remoteSort){
13665             this.applySort();
13666             this.fireEvent("datachanged", this);
13667         }else{
13668             this.load(this.lastOptions);
13669         }
13670     },
13671
13672     /**
13673      * Calls the specified function for each of the Records in the cache.
13674      * @param {Function} fn The function to call. The Record is passed as the first parameter.
13675      * Returning <em>false</em> aborts and exits the iteration.
13676      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13677      */
13678     each : function(fn, scope){
13679         this.data.each(fn, scope);
13680     },
13681
13682     /**
13683      * Gets all records modified since the last commit.  Modified records are persisted across load operations
13684      * (e.g., during paging).
13685      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13686      */
13687     getModifiedRecords : function(){
13688         return this.modified;
13689     },
13690
13691     // private
13692     createFilterFn : function(property, value, anyMatch){
13693         if(!value.exec){ // not a regex
13694             value = String(value);
13695             if(value.length == 0){
13696                 return false;
13697             }
13698             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13699         }
13700         return function(r){
13701             return value.test(r.data[property]);
13702         };
13703     },
13704
13705     /**
13706      * Sums the value of <i>property</i> for each record between start and end and returns the result.
13707      * @param {String} property A field on your records
13708      * @param {Number} start The record index to start at (defaults to 0)
13709      * @param {Number} end The last record index to include (defaults to length - 1)
13710      * @return {Number} The sum
13711      */
13712     sum : function(property, start, end){
13713         var rs = this.data.items, v = 0;
13714         start = start || 0;
13715         end = (end || end === 0) ? end : rs.length-1;
13716
13717         for(var i = start; i <= end; i++){
13718             v += (rs[i].data[property] || 0);
13719         }
13720         return v;
13721     },
13722
13723     /**
13724      * Filter the records by a specified property.
13725      * @param {String} field A field on your records
13726      * @param {String/RegExp} value Either a string that the field
13727      * should start with or a RegExp to test against the field
13728      * @param {Boolean} anyMatch True to match any part not just the beginning
13729      */
13730     filter : function(property, value, anyMatch){
13731         var fn = this.createFilterFn(property, value, anyMatch);
13732         return fn ? this.filterBy(fn) : this.clearFilter();
13733     },
13734
13735     /**
13736      * Filter by a function. The specified function will be called with each
13737      * record in this data source. If the function returns true the record is included,
13738      * otherwise it is filtered.
13739      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13740      * @param {Object} scope (optional) The scope of the function (defaults to this)
13741      */
13742     filterBy : function(fn, scope){
13743         this.snapshot = this.snapshot || this.data;
13744         this.data = this.queryBy(fn, scope||this);
13745         this.fireEvent("datachanged", this);
13746     },
13747
13748     /**
13749      * Query the records by a specified property.
13750      * @param {String} field A field on your records
13751      * @param {String/RegExp} value Either a string that the field
13752      * should start with or a RegExp to test against the field
13753      * @param {Boolean} anyMatch True to match any part not just the beginning
13754      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13755      */
13756     query : function(property, value, anyMatch){
13757         var fn = this.createFilterFn(property, value, anyMatch);
13758         return fn ? this.queryBy(fn) : this.data.clone();
13759     },
13760
13761     /**
13762      * Query by a function. The specified function will be called with each
13763      * record in this data source. If the function returns true the record is included
13764      * in the results.
13765      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13766      * @param {Object} scope (optional) The scope of the function (defaults to this)
13767       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13768      **/
13769     queryBy : function(fn, scope){
13770         var data = this.snapshot || this.data;
13771         return data.filterBy(fn, scope||this);
13772     },
13773
13774     /**
13775      * Collects unique values for a particular dataIndex from this store.
13776      * @param {String} dataIndex The property to collect
13777      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13778      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13779      * @return {Array} An array of the unique values
13780      **/
13781     collect : function(dataIndex, allowNull, bypassFilter){
13782         var d = (bypassFilter === true && this.snapshot) ?
13783                 this.snapshot.items : this.data.items;
13784         var v, sv, r = [], l = {};
13785         for(var i = 0, len = d.length; i < len; i++){
13786             v = d[i].data[dataIndex];
13787             sv = String(v);
13788             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13789                 l[sv] = true;
13790                 r[r.length] = v;
13791             }
13792         }
13793         return r;
13794     },
13795
13796     /**
13797      * Revert to a view of the Record cache with no filtering applied.
13798      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13799      */
13800     clearFilter : function(suppressEvent){
13801         if(this.snapshot && this.snapshot != this.data){
13802             this.data = this.snapshot;
13803             delete this.snapshot;
13804             if(suppressEvent !== true){
13805                 this.fireEvent("datachanged", this);
13806             }
13807         }
13808     },
13809
13810     // private
13811     afterEdit : function(record){
13812         if(this.modified.indexOf(record) == -1){
13813             this.modified.push(record);
13814         }
13815         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13816     },
13817     
13818     // private
13819     afterReject : function(record){
13820         this.modified.remove(record);
13821         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13822     },
13823
13824     // private
13825     afterCommit : function(record){
13826         this.modified.remove(record);
13827         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13828     },
13829
13830     /**
13831      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13832      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13833      */
13834     commitChanges : function(){
13835         var m = this.modified.slice(0);
13836         this.modified = [];
13837         for(var i = 0, len = m.length; i < len; i++){
13838             m[i].commit();
13839         }
13840     },
13841
13842     /**
13843      * Cancel outstanding changes on all changed records.
13844      */
13845     rejectChanges : function(){
13846         var m = this.modified.slice(0);
13847         this.modified = [];
13848         for(var i = 0, len = m.length; i < len; i++){
13849             m[i].reject();
13850         }
13851     },
13852
13853     onMetaChange : function(meta, rtype, o){
13854         this.recordType = rtype;
13855         this.fields = rtype.prototype.fields;
13856         delete this.snapshot;
13857         this.sortInfo = meta.sortInfo || this.sortInfo;
13858         this.modified = [];
13859         this.fireEvent('metachange', this, this.reader.meta);
13860     },
13861     
13862     moveIndex : function(data, type)
13863     {
13864         var index = this.indexOf(data);
13865         
13866         var newIndex = index + type;
13867         
13868         this.remove(data);
13869         
13870         this.insert(newIndex, data);
13871         
13872     }
13873 });/*
13874  * Based on:
13875  * Ext JS Library 1.1.1
13876  * Copyright(c) 2006-2007, Ext JS, LLC.
13877  *
13878  * Originally Released Under LGPL - original licence link has changed is not relivant.
13879  *
13880  * Fork - LGPL
13881  * <script type="text/javascript">
13882  */
13883
13884 /**
13885  * @class Roo.data.SimpleStore
13886  * @extends Roo.data.Store
13887  * Small helper class to make creating Stores from Array data easier.
13888  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13889  * @cfg {Array} fields An array of field definition objects, or field name strings.
13890  * @cfg {Object} an existing reader (eg. copied from another store)
13891  * @cfg {Array} data The multi-dimensional array of data
13892  * @constructor
13893  * @param {Object} config
13894  */
13895 Roo.data.SimpleStore = function(config)
13896 {
13897     Roo.data.SimpleStore.superclass.constructor.call(this, {
13898         isLocal : true,
13899         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13900                 id: config.id
13901             },
13902             Roo.data.Record.create(config.fields)
13903         ),
13904         proxy : new Roo.data.MemoryProxy(config.data)
13905     });
13906     this.load();
13907 };
13908 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13909  * Based on:
13910  * Ext JS Library 1.1.1
13911  * Copyright(c) 2006-2007, Ext JS, LLC.
13912  *
13913  * Originally Released Under LGPL - original licence link has changed is not relivant.
13914  *
13915  * Fork - LGPL
13916  * <script type="text/javascript">
13917  */
13918
13919 /**
13920 /**
13921  * @extends Roo.data.Store
13922  * @class Roo.data.JsonStore
13923  * Small helper class to make creating Stores for JSON data easier. <br/>
13924 <pre><code>
13925 var store = new Roo.data.JsonStore({
13926     url: 'get-images.php',
13927     root: 'images',
13928     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13929 });
13930 </code></pre>
13931  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13932  * JsonReader and HttpProxy (unless inline data is provided).</b>
13933  * @cfg {Array} fields An array of field definition objects, or field name strings.
13934  * @constructor
13935  * @param {Object} config
13936  */
13937 Roo.data.JsonStore = function(c){
13938     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13939         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13940         reader: new Roo.data.JsonReader(c, c.fields)
13941     }));
13942 };
13943 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13944  * Based on:
13945  * Ext JS Library 1.1.1
13946  * Copyright(c) 2006-2007, Ext JS, LLC.
13947  *
13948  * Originally Released Under LGPL - original licence link has changed is not relivant.
13949  *
13950  * Fork - LGPL
13951  * <script type="text/javascript">
13952  */
13953
13954  
13955 Roo.data.Field = function(config){
13956     if(typeof config == "string"){
13957         config = {name: config};
13958     }
13959     Roo.apply(this, config);
13960     
13961     if(!this.type){
13962         this.type = "auto";
13963     }
13964     
13965     var st = Roo.data.SortTypes;
13966     // named sortTypes are supported, here we look them up
13967     if(typeof this.sortType == "string"){
13968         this.sortType = st[this.sortType];
13969     }
13970     
13971     // set default sortType for strings and dates
13972     if(!this.sortType){
13973         switch(this.type){
13974             case "string":
13975                 this.sortType = st.asUCString;
13976                 break;
13977             case "date":
13978                 this.sortType = st.asDate;
13979                 break;
13980             default:
13981                 this.sortType = st.none;
13982         }
13983     }
13984
13985     // define once
13986     var stripRe = /[\$,%]/g;
13987
13988     // prebuilt conversion function for this field, instead of
13989     // switching every time we're reading a value
13990     if(!this.convert){
13991         var cv, dateFormat = this.dateFormat;
13992         switch(this.type){
13993             case "":
13994             case "auto":
13995             case undefined:
13996                 cv = function(v){ return v; };
13997                 break;
13998             case "string":
13999                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14000                 break;
14001             case "int":
14002                 cv = function(v){
14003                     return v !== undefined && v !== null && v !== '' ?
14004                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14005                     };
14006                 break;
14007             case "float":
14008                 cv = function(v){
14009                     return v !== undefined && v !== null && v !== '' ?
14010                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14011                     };
14012                 break;
14013             case "bool":
14014             case "boolean":
14015                 cv = function(v){ return v === true || v === "true" || v == 1; };
14016                 break;
14017             case "date":
14018                 cv = function(v){
14019                     if(!v){
14020                         return '';
14021                     }
14022                     if(v instanceof Date){
14023                         return v;
14024                     }
14025                     if(dateFormat){
14026                         if(dateFormat == "timestamp"){
14027                             return new Date(v*1000);
14028                         }
14029                         return Date.parseDate(v, dateFormat);
14030                     }
14031                     var parsed = Date.parse(v);
14032                     return parsed ? new Date(parsed) : null;
14033                 };
14034              break;
14035             
14036         }
14037         this.convert = cv;
14038     }
14039 };
14040
14041 Roo.data.Field.prototype = {
14042     dateFormat: null,
14043     defaultValue: "",
14044     mapping: null,
14045     sortType : null,
14046     sortDir : "ASC"
14047 };/*
14048  * Based on:
14049  * Ext JS Library 1.1.1
14050  * Copyright(c) 2006-2007, Ext JS, LLC.
14051  *
14052  * Originally Released Under LGPL - original licence link has changed is not relivant.
14053  *
14054  * Fork - LGPL
14055  * <script type="text/javascript">
14056  */
14057  
14058 // Base class for reading structured data from a data source.  This class is intended to be
14059 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14060
14061 /**
14062  * @class Roo.data.DataReader
14063  * Base class for reading structured data from a data source.  This class is intended to be
14064  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14065  */
14066
14067 Roo.data.DataReader = function(meta, recordType){
14068     
14069     this.meta = meta;
14070     
14071     this.recordType = recordType instanceof Array ? 
14072         Roo.data.Record.create(recordType) : recordType;
14073 };
14074
14075 Roo.data.DataReader.prototype = {
14076     
14077     
14078     readerType : 'Data',
14079      /**
14080      * Create an empty record
14081      * @param {Object} data (optional) - overlay some values
14082      * @return {Roo.data.Record} record created.
14083      */
14084     newRow :  function(d) {
14085         var da =  {};
14086         this.recordType.prototype.fields.each(function(c) {
14087             switch( c.type) {
14088                 case 'int' : da[c.name] = 0; break;
14089                 case 'date' : da[c.name] = new Date(); break;
14090                 case 'float' : da[c.name] = 0.0; break;
14091                 case 'boolean' : da[c.name] = false; break;
14092                 default : da[c.name] = ""; break;
14093             }
14094             
14095         });
14096         return new this.recordType(Roo.apply(da, d));
14097     }
14098     
14099     
14100 };/*
14101  * Based on:
14102  * Ext JS Library 1.1.1
14103  * Copyright(c) 2006-2007, Ext JS, LLC.
14104  *
14105  * Originally Released Under LGPL - original licence link has changed is not relivant.
14106  *
14107  * Fork - LGPL
14108  * <script type="text/javascript">
14109  */
14110
14111 /**
14112  * @class Roo.data.DataProxy
14113  * @extends Roo.data.Observable
14114  * This class is an abstract base class for implementations which provide retrieval of
14115  * unformatted data objects.<br>
14116  * <p>
14117  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14118  * (of the appropriate type which knows how to parse the data object) to provide a block of
14119  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14120  * <p>
14121  * Custom implementations must implement the load method as described in
14122  * {@link Roo.data.HttpProxy#load}.
14123  */
14124 Roo.data.DataProxy = function(){
14125     this.addEvents({
14126         /**
14127          * @event beforeload
14128          * Fires before a network request is made to retrieve a data object.
14129          * @param {Object} This DataProxy object.
14130          * @param {Object} params The params parameter to the load function.
14131          */
14132         beforeload : true,
14133         /**
14134          * @event load
14135          * Fires before the load method's callback is called.
14136          * @param {Object} This DataProxy object.
14137          * @param {Object} o The data object.
14138          * @param {Object} arg The callback argument object passed to the load function.
14139          */
14140         load : true,
14141         /**
14142          * @event loadexception
14143          * Fires if an Exception occurs during data retrieval.
14144          * @param {Object} This DataProxy object.
14145          * @param {Object} o The data object.
14146          * @param {Object} arg The callback argument object passed to the load function.
14147          * @param {Object} e The Exception.
14148          */
14149         loadexception : true
14150     });
14151     Roo.data.DataProxy.superclass.constructor.call(this);
14152 };
14153
14154 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14155
14156     /**
14157      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14158      */
14159 /*
14160  * Based on:
14161  * Ext JS Library 1.1.1
14162  * Copyright(c) 2006-2007, Ext JS, LLC.
14163  *
14164  * Originally Released Under LGPL - original licence link has changed is not relivant.
14165  *
14166  * Fork - LGPL
14167  * <script type="text/javascript">
14168  */
14169 /**
14170  * @class Roo.data.MemoryProxy
14171  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14172  * to the Reader when its load method is called.
14173  * @constructor
14174  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14175  */
14176 Roo.data.MemoryProxy = function(data){
14177     if (data.data) {
14178         data = data.data;
14179     }
14180     Roo.data.MemoryProxy.superclass.constructor.call(this);
14181     this.data = data;
14182 };
14183
14184 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14185     
14186     /**
14187      * Load data from the requested source (in this case an in-memory
14188      * data object passed to the constructor), read the data object into
14189      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14190      * process that block using the passed callback.
14191      * @param {Object} params This parameter is not used by the MemoryProxy class.
14192      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14193      * object into a block of Roo.data.Records.
14194      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14195      * The function must be passed <ul>
14196      * <li>The Record block object</li>
14197      * <li>The "arg" argument from the load function</li>
14198      * <li>A boolean success indicator</li>
14199      * </ul>
14200      * @param {Object} scope The scope in which to call the callback
14201      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14202      */
14203     load : function(params, reader, callback, scope, arg){
14204         params = params || {};
14205         var result;
14206         try {
14207             result = reader.readRecords(params.data ? params.data :this.data);
14208         }catch(e){
14209             this.fireEvent("loadexception", this, arg, null, e);
14210             callback.call(scope, null, arg, false);
14211             return;
14212         }
14213         callback.call(scope, result, arg, true);
14214     },
14215     
14216     // private
14217     update : function(params, records){
14218         
14219     }
14220 });/*
14221  * Based on:
14222  * Ext JS Library 1.1.1
14223  * Copyright(c) 2006-2007, Ext JS, LLC.
14224  *
14225  * Originally Released Under LGPL - original licence link has changed is not relivant.
14226  *
14227  * Fork - LGPL
14228  * <script type="text/javascript">
14229  */
14230 /**
14231  * @class Roo.data.HttpProxy
14232  * @extends Roo.data.DataProxy
14233  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14234  * configured to reference a certain URL.<br><br>
14235  * <p>
14236  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14237  * from which the running page was served.<br><br>
14238  * <p>
14239  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14240  * <p>
14241  * Be aware that to enable the browser to parse an XML document, the server must set
14242  * the Content-Type header in the HTTP response to "text/xml".
14243  * @constructor
14244  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14245  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14246  * will be used to make the request.
14247  */
14248 Roo.data.HttpProxy = function(conn){
14249     Roo.data.HttpProxy.superclass.constructor.call(this);
14250     // is conn a conn config or a real conn?
14251     this.conn = conn;
14252     this.useAjax = !conn || !conn.events;
14253   
14254 };
14255
14256 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14257     // thse are take from connection...
14258     
14259     /**
14260      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14261      */
14262     /**
14263      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14264      * extra parameters to each request made by this object. (defaults to undefined)
14265      */
14266     /**
14267      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14268      *  to each request made by this object. (defaults to undefined)
14269      */
14270     /**
14271      * @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)
14272      */
14273     /**
14274      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14275      */
14276      /**
14277      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14278      * @type Boolean
14279      */
14280   
14281
14282     /**
14283      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14284      * @type Boolean
14285      */
14286     /**
14287      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14288      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14289      * a finer-grained basis than the DataProxy events.
14290      */
14291     getConnection : function(){
14292         return this.useAjax ? Roo.Ajax : this.conn;
14293     },
14294
14295     /**
14296      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14297      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14298      * process that block using the passed callback.
14299      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14300      * for the request to the remote server.
14301      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14302      * object into a block of Roo.data.Records.
14303      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14304      * The function must be passed <ul>
14305      * <li>The Record block object</li>
14306      * <li>The "arg" argument from the load function</li>
14307      * <li>A boolean success indicator</li>
14308      * </ul>
14309      * @param {Object} scope The scope in which to call the callback
14310      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14311      */
14312     load : function(params, reader, callback, scope, arg){
14313         if(this.fireEvent("beforeload", this, params) !== false){
14314             var  o = {
14315                 params : params || {},
14316                 request: {
14317                     callback : callback,
14318                     scope : scope,
14319                     arg : arg
14320                 },
14321                 reader: reader,
14322                 callback : this.loadResponse,
14323                 scope: this
14324             };
14325             if(this.useAjax){
14326                 Roo.applyIf(o, this.conn);
14327                 if(this.activeRequest){
14328                     Roo.Ajax.abort(this.activeRequest);
14329                 }
14330                 this.activeRequest = Roo.Ajax.request(o);
14331             }else{
14332                 this.conn.request(o);
14333             }
14334         }else{
14335             callback.call(scope||this, null, arg, false);
14336         }
14337     },
14338
14339     // private
14340     loadResponse : function(o, success, response){
14341         delete this.activeRequest;
14342         if(!success){
14343             this.fireEvent("loadexception", this, o, response);
14344             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14345             return;
14346         }
14347         var result;
14348         try {
14349             result = o.reader.read(response);
14350         }catch(e){
14351             this.fireEvent("loadexception", this, o, response, e);
14352             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14353             return;
14354         }
14355         
14356         this.fireEvent("load", this, o, o.request.arg);
14357         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14358     },
14359
14360     // private
14361     update : function(dataSet){
14362
14363     },
14364
14365     // private
14366     updateResponse : function(dataSet){
14367
14368     }
14369 });/*
14370  * Based on:
14371  * Ext JS Library 1.1.1
14372  * Copyright(c) 2006-2007, Ext JS, LLC.
14373  *
14374  * Originally Released Under LGPL - original licence link has changed is not relivant.
14375  *
14376  * Fork - LGPL
14377  * <script type="text/javascript">
14378  */
14379
14380 /**
14381  * @class Roo.data.ScriptTagProxy
14382  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14383  * other than the originating domain of the running page.<br><br>
14384  * <p>
14385  * <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
14386  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14387  * <p>
14388  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14389  * source code that is used as the source inside a &lt;script> tag.<br><br>
14390  * <p>
14391  * In order for the browser to process the returned data, the server must wrap the data object
14392  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14393  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14394  * depending on whether the callback name was passed:
14395  * <p>
14396  * <pre><code>
14397 boolean scriptTag = false;
14398 String cb = request.getParameter("callback");
14399 if (cb != null) {
14400     scriptTag = true;
14401     response.setContentType("text/javascript");
14402 } else {
14403     response.setContentType("application/x-json");
14404 }
14405 Writer out = response.getWriter();
14406 if (scriptTag) {
14407     out.write(cb + "(");
14408 }
14409 out.print(dataBlock.toJsonString());
14410 if (scriptTag) {
14411     out.write(");");
14412 }
14413 </pre></code>
14414  *
14415  * @constructor
14416  * @param {Object} config A configuration object.
14417  */
14418 Roo.data.ScriptTagProxy = function(config){
14419     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14420     Roo.apply(this, config);
14421     this.head = document.getElementsByTagName("head")[0];
14422 };
14423
14424 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14425
14426 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14427     /**
14428      * @cfg {String} url The URL from which to request the data object.
14429      */
14430     /**
14431      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14432      */
14433     timeout : 30000,
14434     /**
14435      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14436      * the server the name of the callback function set up by the load call to process the returned data object.
14437      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14438      * javascript output which calls this named function passing the data object as its only parameter.
14439      */
14440     callbackParam : "callback",
14441     /**
14442      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14443      * name to the request.
14444      */
14445     nocache : true,
14446
14447     /**
14448      * Load data from the configured URL, read the data object into
14449      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14450      * process that block using the passed callback.
14451      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14452      * for the request to the remote server.
14453      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14454      * object into a block of Roo.data.Records.
14455      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14456      * The function must be passed <ul>
14457      * <li>The Record block object</li>
14458      * <li>The "arg" argument from the load function</li>
14459      * <li>A boolean success indicator</li>
14460      * </ul>
14461      * @param {Object} scope The scope in which to call the callback
14462      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14463      */
14464     load : function(params, reader, callback, scope, arg){
14465         if(this.fireEvent("beforeload", this, params) !== false){
14466
14467             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14468
14469             var url = this.url;
14470             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14471             if(this.nocache){
14472                 url += "&_dc=" + (new Date().getTime());
14473             }
14474             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14475             var trans = {
14476                 id : transId,
14477                 cb : "stcCallback"+transId,
14478                 scriptId : "stcScript"+transId,
14479                 params : params,
14480                 arg : arg,
14481                 url : url,
14482                 callback : callback,
14483                 scope : scope,
14484                 reader : reader
14485             };
14486             var conn = this;
14487
14488             window[trans.cb] = function(o){
14489                 conn.handleResponse(o, trans);
14490             };
14491
14492             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14493
14494             if(this.autoAbort !== false){
14495                 this.abort();
14496             }
14497
14498             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14499
14500             var script = document.createElement("script");
14501             script.setAttribute("src", url);
14502             script.setAttribute("type", "text/javascript");
14503             script.setAttribute("id", trans.scriptId);
14504             this.head.appendChild(script);
14505
14506             this.trans = trans;
14507         }else{
14508             callback.call(scope||this, null, arg, false);
14509         }
14510     },
14511
14512     // private
14513     isLoading : function(){
14514         return this.trans ? true : false;
14515     },
14516
14517     /**
14518      * Abort the current server request.
14519      */
14520     abort : function(){
14521         if(this.isLoading()){
14522             this.destroyTrans(this.trans);
14523         }
14524     },
14525
14526     // private
14527     destroyTrans : function(trans, isLoaded){
14528         this.head.removeChild(document.getElementById(trans.scriptId));
14529         clearTimeout(trans.timeoutId);
14530         if(isLoaded){
14531             window[trans.cb] = undefined;
14532             try{
14533                 delete window[trans.cb];
14534             }catch(e){}
14535         }else{
14536             // if hasn't been loaded, wait for load to remove it to prevent script error
14537             window[trans.cb] = function(){
14538                 window[trans.cb] = undefined;
14539                 try{
14540                     delete window[trans.cb];
14541                 }catch(e){}
14542             };
14543         }
14544     },
14545
14546     // private
14547     handleResponse : function(o, trans){
14548         this.trans = false;
14549         this.destroyTrans(trans, true);
14550         var result;
14551         try {
14552             result = trans.reader.readRecords(o);
14553         }catch(e){
14554             this.fireEvent("loadexception", this, o, trans.arg, e);
14555             trans.callback.call(trans.scope||window, null, trans.arg, false);
14556             return;
14557         }
14558         this.fireEvent("load", this, o, trans.arg);
14559         trans.callback.call(trans.scope||window, result, trans.arg, true);
14560     },
14561
14562     // private
14563     handleFailure : function(trans){
14564         this.trans = false;
14565         this.destroyTrans(trans, false);
14566         this.fireEvent("loadexception", this, null, trans.arg);
14567         trans.callback.call(trans.scope||window, null, trans.arg, false);
14568     }
14569 });/*
14570  * Based on:
14571  * Ext JS Library 1.1.1
14572  * Copyright(c) 2006-2007, Ext JS, LLC.
14573  *
14574  * Originally Released Under LGPL - original licence link has changed is not relivant.
14575  *
14576  * Fork - LGPL
14577  * <script type="text/javascript">
14578  */
14579
14580 /**
14581  * @class Roo.data.JsonReader
14582  * @extends Roo.data.DataReader
14583  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14584  * based on mappings in a provided Roo.data.Record constructor.
14585  * 
14586  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14587  * in the reply previously. 
14588  * 
14589  * <p>
14590  * Example code:
14591  * <pre><code>
14592 var RecordDef = Roo.data.Record.create([
14593     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14594     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14595 ]);
14596 var myReader = new Roo.data.JsonReader({
14597     totalProperty: "results",    // The property which contains the total dataset size (optional)
14598     root: "rows",                // The property which contains an Array of row objects
14599     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14600 }, RecordDef);
14601 </code></pre>
14602  * <p>
14603  * This would consume a JSON file like this:
14604  * <pre><code>
14605 { 'results': 2, 'rows': [
14606     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14607     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14608 }
14609 </code></pre>
14610  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14611  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14612  * paged from the remote server.
14613  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14614  * @cfg {String} root name of the property which contains the Array of row objects.
14615  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14616  * @cfg {Array} fields Array of field definition objects
14617  * @constructor
14618  * Create a new JsonReader
14619  * @param {Object} meta Metadata configuration options
14620  * @param {Object} recordType Either an Array of field definition objects,
14621  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14622  */
14623 Roo.data.JsonReader = function(meta, recordType){
14624     
14625     meta = meta || {};
14626     // set some defaults:
14627     Roo.applyIf(meta, {
14628         totalProperty: 'total',
14629         successProperty : 'success',
14630         root : 'data',
14631         id : 'id'
14632     });
14633     
14634     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14635 };
14636 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14637     
14638     readerType : 'Json',
14639     
14640     /**
14641      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14642      * Used by Store query builder to append _requestMeta to params.
14643      * 
14644      */
14645     metaFromRemote : false,
14646     /**
14647      * This method is only used by a DataProxy which has retrieved data from a remote server.
14648      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14649      * @return {Object} data A data block which is used by an Roo.data.Store object as
14650      * a cache of Roo.data.Records.
14651      */
14652     read : function(response){
14653         var json = response.responseText;
14654        
14655         var o = /* eval:var:o */ eval("("+json+")");
14656         if(!o) {
14657             throw {message: "JsonReader.read: Json object not found"};
14658         }
14659         
14660         if(o.metaData){
14661             
14662             delete this.ef;
14663             this.metaFromRemote = true;
14664             this.meta = o.metaData;
14665             this.recordType = Roo.data.Record.create(o.metaData.fields);
14666             this.onMetaChange(this.meta, this.recordType, o);
14667         }
14668         return this.readRecords(o);
14669     },
14670
14671     // private function a store will implement
14672     onMetaChange : function(meta, recordType, o){
14673
14674     },
14675
14676     /**
14677          * @ignore
14678          */
14679     simpleAccess: function(obj, subsc) {
14680         return obj[subsc];
14681     },
14682
14683         /**
14684          * @ignore
14685          */
14686     getJsonAccessor: function(){
14687         var re = /[\[\.]/;
14688         return function(expr) {
14689             try {
14690                 return(re.test(expr))
14691                     ? new Function("obj", "return obj." + expr)
14692                     : function(obj){
14693                         return obj[expr];
14694                     };
14695             } catch(e){}
14696             return Roo.emptyFn;
14697         };
14698     }(),
14699
14700     /**
14701      * Create a data block containing Roo.data.Records from an XML document.
14702      * @param {Object} o An object which contains an Array of row objects in the property specified
14703      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14704      * which contains the total size of the dataset.
14705      * @return {Object} data A data block which is used by an Roo.data.Store object as
14706      * a cache of Roo.data.Records.
14707      */
14708     readRecords : function(o){
14709         /**
14710          * After any data loads, the raw JSON data is available for further custom processing.
14711          * @type Object
14712          */
14713         this.o = o;
14714         var s = this.meta, Record = this.recordType,
14715             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14716
14717 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
14718         if (!this.ef) {
14719             if(s.totalProperty) {
14720                     this.getTotal = this.getJsonAccessor(s.totalProperty);
14721                 }
14722                 if(s.successProperty) {
14723                     this.getSuccess = this.getJsonAccessor(s.successProperty);
14724                 }
14725                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14726                 if (s.id) {
14727                         var g = this.getJsonAccessor(s.id);
14728                         this.getId = function(rec) {
14729                                 var r = g(rec);  
14730                                 return (r === undefined || r === "") ? null : r;
14731                         };
14732                 } else {
14733                         this.getId = function(){return null;};
14734                 }
14735             this.ef = [];
14736             for(var jj = 0; jj < fl; jj++){
14737                 f = fi[jj];
14738                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14739                 this.ef[jj] = this.getJsonAccessor(map);
14740             }
14741         }
14742
14743         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14744         if(s.totalProperty){
14745             var vt = parseInt(this.getTotal(o), 10);
14746             if(!isNaN(vt)){
14747                 totalRecords = vt;
14748             }
14749         }
14750         if(s.successProperty){
14751             var vs = this.getSuccess(o);
14752             if(vs === false || vs === 'false'){
14753                 success = false;
14754             }
14755         }
14756         var records = [];
14757         for(var i = 0; i < c; i++){
14758                 var n = root[i];
14759             var values = {};
14760             var id = this.getId(n);
14761             for(var j = 0; j < fl; j++){
14762                 f = fi[j];
14763             var v = this.ef[j](n);
14764             if (!f.convert) {
14765                 Roo.log('missing convert for ' + f.name);
14766                 Roo.log(f);
14767                 continue;
14768             }
14769             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14770             }
14771             var record = new Record(values, id);
14772             record.json = n;
14773             records[i] = record;
14774         }
14775         return {
14776             raw : o,
14777             success : success,
14778             records : records,
14779             totalRecords : totalRecords
14780         };
14781     },
14782     // used when loading children.. @see loadDataFromChildren
14783     toLoadData: function(rec)
14784     {
14785         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14786         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14787         return { data : data, total : data.length };
14788         
14789     }
14790 });/*
14791  * Based on:
14792  * Ext JS Library 1.1.1
14793  * Copyright(c) 2006-2007, Ext JS, LLC.
14794  *
14795  * Originally Released Under LGPL - original licence link has changed is not relivant.
14796  *
14797  * Fork - LGPL
14798  * <script type="text/javascript">
14799  */
14800
14801 /**
14802  * @class Roo.data.ArrayReader
14803  * @extends Roo.data.DataReader
14804  * Data reader class to create an Array of Roo.data.Record objects from an Array.
14805  * Each element of that Array represents a row of data fields. The
14806  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14807  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14808  * <p>
14809  * Example code:.
14810  * <pre><code>
14811 var RecordDef = Roo.data.Record.create([
14812     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
14813     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
14814 ]);
14815 var myReader = new Roo.data.ArrayReader({
14816     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
14817 }, RecordDef);
14818 </code></pre>
14819  * <p>
14820  * This would consume an Array like this:
14821  * <pre><code>
14822 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14823   </code></pre>
14824  
14825  * @constructor
14826  * Create a new JsonReader
14827  * @param {Object} meta Metadata configuration options.
14828  * @param {Object|Array} recordType Either an Array of field definition objects
14829  * 
14830  * @cfg {Array} fields Array of field definition objects
14831  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14832  * as specified to {@link Roo.data.Record#create},
14833  * or an {@link Roo.data.Record} object
14834  *
14835  * 
14836  * created using {@link Roo.data.Record#create}.
14837  */
14838 Roo.data.ArrayReader = function(meta, recordType)
14839 {    
14840     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14841 };
14842
14843 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14844     
14845       /**
14846      * Create a data block containing Roo.data.Records from an XML document.
14847      * @param {Object} o An Array of row objects which represents the dataset.
14848      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14849      * a cache of Roo.data.Records.
14850      */
14851     readRecords : function(o)
14852     {
14853         var sid = this.meta ? this.meta.id : null;
14854         var recordType = this.recordType, fields = recordType.prototype.fields;
14855         var records = [];
14856         var root = o;
14857         for(var i = 0; i < root.length; i++){
14858                 var n = root[i];
14859             var values = {};
14860             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14861             for(var j = 0, jlen = fields.length; j < jlen; j++){
14862                 var f = fields.items[j];
14863                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14864                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14865                 v = f.convert(v);
14866                 values[f.name] = v;
14867             }
14868             var record = new recordType(values, id);
14869             record.json = n;
14870             records[records.length] = record;
14871         }
14872         return {
14873             records : records,
14874             totalRecords : records.length
14875         };
14876     },
14877     // used when loading children.. @see loadDataFromChildren
14878     toLoadData: function(rec)
14879     {
14880         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14881         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14882         
14883     }
14884     
14885     
14886 });/*
14887  * - LGPL
14888  * * 
14889  */
14890
14891 /**
14892  * @class Roo.bootstrap.ComboBox
14893  * @extends Roo.bootstrap.TriggerField
14894  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14895  * @cfg {Boolean} append (true|false) default false
14896  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14897  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14898  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14899  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14900  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14901  * @cfg {Boolean} animate default true
14902  * @cfg {Boolean} emptyResultText only for touch device
14903  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14904  * @cfg {String} emptyTitle default ''
14905  * @cfg {Number} width fixed with? experimental
14906  * @constructor
14907  * Create a new ComboBox.
14908  * @param {Object} config Configuration options
14909  */
14910 Roo.bootstrap.ComboBox = function(config){
14911     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14912     this.addEvents({
14913         /**
14914          * @event expand
14915          * Fires when the dropdown list is expanded
14916         * @param {Roo.bootstrap.ComboBox} combo This combo box
14917         */
14918         'expand' : true,
14919         /**
14920          * @event collapse
14921          * Fires when the dropdown list is collapsed
14922         * @param {Roo.bootstrap.ComboBox} combo This combo box
14923         */
14924         'collapse' : true,
14925         /**
14926          * @event beforeselect
14927          * Fires before a list item is selected. Return false to cancel the selection.
14928         * @param {Roo.bootstrap.ComboBox} combo This combo box
14929         * @param {Roo.data.Record} record The data record returned from the underlying store
14930         * @param {Number} index The index of the selected item in the dropdown list
14931         */
14932         'beforeselect' : true,
14933         /**
14934          * @event select
14935          * Fires when a list item is selected
14936         * @param {Roo.bootstrap.ComboBox} combo This combo box
14937         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14938         * @param {Number} index The index of the selected item in the dropdown list
14939         */
14940         'select' : true,
14941         /**
14942          * @event beforequery
14943          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14944          * The event object passed has these properties:
14945         * @param {Roo.bootstrap.ComboBox} combo This combo box
14946         * @param {String} query The query
14947         * @param {Boolean} forceAll true to force "all" query
14948         * @param {Boolean} cancel true to cancel the query
14949         * @param {Object} e The query event object
14950         */
14951         'beforequery': true,
14952          /**
14953          * @event add
14954          * Fires when the 'add' icon is pressed (add a listener to enable add button)
14955         * @param {Roo.bootstrap.ComboBox} combo This combo box
14956         */
14957         'add' : true,
14958         /**
14959          * @event edit
14960          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14961         * @param {Roo.bootstrap.ComboBox} combo This combo box
14962         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14963         */
14964         'edit' : true,
14965         /**
14966          * @event remove
14967          * Fires when the remove value from the combobox array
14968         * @param {Roo.bootstrap.ComboBox} combo This combo box
14969         */
14970         'remove' : true,
14971         /**
14972          * @event afterremove
14973          * Fires when the remove value from the combobox array
14974         * @param {Roo.bootstrap.ComboBox} combo This combo box
14975         */
14976         'afterremove' : true,
14977         /**
14978          * @event specialfilter
14979          * Fires when specialfilter
14980             * @param {Roo.bootstrap.ComboBox} combo This combo box
14981             */
14982         'specialfilter' : true,
14983         /**
14984          * @event tick
14985          * Fires when tick the element
14986             * @param {Roo.bootstrap.ComboBox} combo This combo box
14987             */
14988         'tick' : true,
14989         /**
14990          * @event touchviewdisplay
14991          * Fires when touch view require special display (default is using displayField)
14992             * @param {Roo.bootstrap.ComboBox} combo This combo box
14993             * @param {Object} cfg set html .
14994             */
14995         'touchviewdisplay' : true
14996         
14997     });
14998     
14999     this.item = [];
15000     this.tickItems = [];
15001     
15002     this.selectedIndex = -1;
15003     if(this.mode == 'local'){
15004         if(config.queryDelay === undefined){
15005             this.queryDelay = 10;
15006         }
15007         if(config.minChars === undefined){
15008             this.minChars = 0;
15009         }
15010     }
15011 };
15012
15013 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15014      
15015     /**
15016      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15017      * rendering into an Roo.Editor, defaults to false)
15018      */
15019     /**
15020      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15021      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15022      */
15023     /**
15024      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15025      */
15026     /**
15027      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15028      * the dropdown list (defaults to undefined, with no header element)
15029      */
15030
15031      /**
15032      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15033      */
15034      
15035      /**
15036      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15037      */
15038     listWidth: undefined,
15039     /**
15040      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15041      * mode = 'remote' or 'text' if mode = 'local')
15042      */
15043     displayField: undefined,
15044     
15045     /**
15046      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15047      * mode = 'remote' or 'value' if mode = 'local'). 
15048      * Note: use of a valueField requires the user make a selection
15049      * in order for a value to be mapped.
15050      */
15051     valueField: undefined,
15052     /**
15053      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15054      */
15055     modalTitle : '',
15056     
15057     /**
15058      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15059      * field's data value (defaults to the underlying DOM element's name)
15060      */
15061     hiddenName: undefined,
15062     /**
15063      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15064      */
15065     listClass: '',
15066     /**
15067      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15068      */
15069     selectedClass: 'active',
15070     
15071     /**
15072      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15073      */
15074     shadow:'sides',
15075     /**
15076      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15077      * anchor positions (defaults to 'tl-bl')
15078      */
15079     listAlign: 'tl-bl?',
15080     /**
15081      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15082      */
15083     maxHeight: 300,
15084     /**
15085      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15086      * query specified by the allQuery config option (defaults to 'query')
15087      */
15088     triggerAction: 'query',
15089     /**
15090      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15091      * (defaults to 4, does not apply if editable = false)
15092      */
15093     minChars : 4,
15094     /**
15095      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15096      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15097      */
15098     typeAhead: false,
15099     /**
15100      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15101      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15102      */
15103     queryDelay: 500,
15104     /**
15105      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15106      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15107      */
15108     pageSize: 0,
15109     /**
15110      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15111      * when editable = true (defaults to false)
15112      */
15113     selectOnFocus:false,
15114     /**
15115      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15116      */
15117     queryParam: 'query',
15118     /**
15119      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15120      * when mode = 'remote' (defaults to 'Loading...')
15121      */
15122     loadingText: 'Loading...',
15123     /**
15124      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15125      */
15126     resizable: false,
15127     /**
15128      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15129      */
15130     handleHeight : 8,
15131     /**
15132      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15133      * traditional select (defaults to true)
15134      */
15135     editable: true,
15136     /**
15137      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15138      */
15139     allQuery: '',
15140     /**
15141      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15142      */
15143     mode: 'remote',
15144     /**
15145      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15146      * listWidth has a higher value)
15147      */
15148     minListWidth : 70,
15149     /**
15150      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15151      * allow the user to set arbitrary text into the field (defaults to false)
15152      */
15153     forceSelection:false,
15154     /**
15155      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15156      * if typeAhead = true (defaults to 250)
15157      */
15158     typeAheadDelay : 250,
15159     /**
15160      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15161      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15162      */
15163     valueNotFoundText : undefined,
15164     /**
15165      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15166      */
15167     blockFocus : false,
15168     
15169     /**
15170      * @cfg {Boolean} disableClear Disable showing of clear button.
15171      */
15172     disableClear : false,
15173     /**
15174      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15175      */
15176     alwaysQuery : false,
15177     
15178     /**
15179      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15180      */
15181     multiple : false,
15182     
15183     /**
15184      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15185      */
15186     invalidClass : "has-warning",
15187     
15188     /**
15189      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15190      */
15191     validClass : "has-success",
15192     
15193     /**
15194      * @cfg {Boolean} specialFilter (true|false) special filter default false
15195      */
15196     specialFilter : false,
15197     
15198     /**
15199      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15200      */
15201     mobileTouchView : true,
15202     
15203     /**
15204      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15205      */
15206     useNativeIOS : false,
15207     
15208     /**
15209      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15210      */
15211     mobile_restrict_height : false,
15212     
15213     ios_options : false,
15214     
15215     //private
15216     addicon : false,
15217     editicon: false,
15218     
15219     page: 0,
15220     hasQuery: false,
15221     append: false,
15222     loadNext: false,
15223     autoFocus : true,
15224     tickable : false,
15225     btnPosition : 'right',
15226     triggerList : true,
15227     showToggleBtn : true,
15228     animate : true,
15229     emptyResultText: 'Empty',
15230     triggerText : 'Select',
15231     emptyTitle : '',
15232     width : false,
15233     
15234     // element that contains real text value.. (when hidden is used..)
15235     
15236     getAutoCreate : function()
15237     {   
15238         var cfg = false;
15239         //render
15240         /*
15241          * Render classic select for iso
15242          */
15243         
15244         if(Roo.isIOS && this.useNativeIOS){
15245             cfg = this.getAutoCreateNativeIOS();
15246             return cfg;
15247         }
15248         
15249         /*
15250          * Touch Devices
15251          */
15252         
15253         if(Roo.isTouch && this.mobileTouchView){
15254             cfg = this.getAutoCreateTouchView();
15255             return cfg;;
15256         }
15257         
15258         /*
15259          *  Normal ComboBox
15260          */
15261         if(!this.tickable){
15262             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15263             return cfg;
15264         }
15265         
15266         /*
15267          *  ComboBox with tickable selections
15268          */
15269              
15270         var align = this.labelAlign || this.parentLabelAlign();
15271         
15272         cfg = {
15273             cls : 'form-group roo-combobox-tickable' //input-group
15274         };
15275         
15276         var btn_text_select = '';
15277         var btn_text_done = '';
15278         var btn_text_cancel = '';
15279         
15280         if (this.btn_text_show) {
15281             btn_text_select = 'Select';
15282             btn_text_done = 'Done';
15283             btn_text_cancel = 'Cancel'; 
15284         }
15285         
15286         var buttons = {
15287             tag : 'div',
15288             cls : 'tickable-buttons',
15289             cn : [
15290                 {
15291                     tag : 'button',
15292                     type : 'button',
15293                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15294                     //html : this.triggerText
15295                     html: btn_text_select
15296                 },
15297                 {
15298                     tag : 'button',
15299                     type : 'button',
15300                     name : 'ok',
15301                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15302                     //html : 'Done'
15303                     html: btn_text_done
15304                 },
15305                 {
15306                     tag : 'button',
15307                     type : 'button',
15308                     name : 'cancel',
15309                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15310                     //html : 'Cancel'
15311                     html: btn_text_cancel
15312                 }
15313             ]
15314         };
15315         
15316         if(this.editable){
15317             buttons.cn.unshift({
15318                 tag: 'input',
15319                 cls: 'roo-select2-search-field-input'
15320             });
15321         }
15322         
15323         var _this = this;
15324         
15325         Roo.each(buttons.cn, function(c){
15326             if (_this.size) {
15327                 c.cls += ' btn-' + _this.size;
15328             }
15329
15330             if (_this.disabled) {
15331                 c.disabled = true;
15332             }
15333         });
15334         
15335         var box = {
15336             tag: 'div',
15337             style : 'display: contents',
15338             cn: [
15339                 {
15340                     tag: 'input',
15341                     type : 'hidden',
15342                     cls: 'form-hidden-field'
15343                 },
15344                 {
15345                     tag: 'ul',
15346                     cls: 'roo-select2-choices',
15347                     cn:[
15348                         {
15349                             tag: 'li',
15350                             cls: 'roo-select2-search-field',
15351                             cn: [
15352                                 buttons
15353                             ]
15354                         }
15355                     ]
15356                 }
15357             ]
15358         };
15359         
15360         var combobox = {
15361             cls: 'roo-select2-container input-group roo-select2-container-multi',
15362             cn: [
15363                 
15364                 box
15365 //                {
15366 //                    tag: 'ul',
15367 //                    cls: 'typeahead typeahead-long dropdown-menu',
15368 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15369 //                }
15370             ]
15371         };
15372         
15373         if(this.hasFeedback && !this.allowBlank){
15374             
15375             var feedback = {
15376                 tag: 'span',
15377                 cls: 'glyphicon form-control-feedback'
15378             };
15379
15380             combobox.cn.push(feedback);
15381         }
15382         
15383         
15384         
15385         var indicator = {
15386             tag : 'i',
15387             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15388             tooltip : 'This field is required'
15389         };
15390         if (Roo.bootstrap.version == 4) {
15391             indicator = {
15392                 tag : 'i',
15393                 style : 'display:none'
15394             };
15395         }
15396         if (align ==='left' && this.fieldLabel.length) {
15397             
15398             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15399             
15400             cfg.cn = [
15401                 indicator,
15402                 {
15403                     tag: 'label',
15404                     'for' :  id,
15405                     cls : 'control-label col-form-label',
15406                     html : this.fieldLabel
15407
15408                 },
15409                 {
15410                     cls : "", 
15411                     cn: [
15412                         combobox
15413                     ]
15414                 }
15415
15416             ];
15417             
15418             var labelCfg = cfg.cn[1];
15419             var contentCfg = cfg.cn[2];
15420             
15421
15422             if(this.indicatorpos == 'right'){
15423                 
15424                 cfg.cn = [
15425                     {
15426                         tag: 'label',
15427                         'for' :  id,
15428                         cls : 'control-label col-form-label',
15429                         cn : [
15430                             {
15431                                 tag : 'span',
15432                                 html : this.fieldLabel
15433                             },
15434                             indicator
15435                         ]
15436                     },
15437                     {
15438                         cls : "",
15439                         cn: [
15440                             combobox
15441                         ]
15442                     }
15443
15444                 ];
15445                 
15446                 
15447                 
15448                 labelCfg = cfg.cn[0];
15449                 contentCfg = cfg.cn[1];
15450             
15451             }
15452             
15453             if(this.labelWidth > 12){
15454                 labelCfg.style = "width: " + this.labelWidth + 'px';
15455             }
15456             if(this.width * 1 > 0){
15457                 contentCfg.style = "width: " + this.width + 'px';
15458             }
15459             if(this.labelWidth < 13 && this.labelmd == 0){
15460                 this.labelmd = this.labelWidth;
15461             }
15462             
15463             if(this.labellg > 0){
15464                 labelCfg.cls += ' col-lg-' + this.labellg;
15465                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15466             }
15467             
15468             if(this.labelmd > 0){
15469                 labelCfg.cls += ' col-md-' + this.labelmd;
15470                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15471             }
15472             
15473             if(this.labelsm > 0){
15474                 labelCfg.cls += ' col-sm-' + this.labelsm;
15475                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15476             }
15477             
15478             if(this.labelxs > 0){
15479                 labelCfg.cls += ' col-xs-' + this.labelxs;
15480                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15481             }
15482                 
15483                 
15484         } else if ( this.fieldLabel.length) {
15485 //                Roo.log(" label");
15486                  cfg.cn = [
15487                    indicator,
15488                     {
15489                         tag: 'label',
15490                         //cls : 'input-group-addon',
15491                         html : this.fieldLabel
15492                     },
15493                     combobox
15494                 ];
15495                 
15496                 if(this.indicatorpos == 'right'){
15497                     cfg.cn = [
15498                         {
15499                             tag: 'label',
15500                             //cls : 'input-group-addon',
15501                             html : this.fieldLabel
15502                         },
15503                         indicator,
15504                         combobox
15505                     ];
15506                     
15507                 }
15508
15509         } else {
15510             
15511 //                Roo.log(" no label && no align");
15512                 cfg = combobox
15513                      
15514                 
15515         }
15516          
15517         var settings=this;
15518         ['xs','sm','md','lg'].map(function(size){
15519             if (settings[size]) {
15520                 cfg.cls += ' col-' + size + '-' + settings[size];
15521             }
15522         });
15523         
15524         return cfg;
15525         
15526     },
15527     
15528     _initEventsCalled : false,
15529     
15530     // private
15531     initEvents: function()
15532     {   
15533         if (this._initEventsCalled) { // as we call render... prevent looping...
15534             return;
15535         }
15536         this._initEventsCalled = true;
15537         
15538         if (!this.store) {
15539             throw "can not find store for combo";
15540         }
15541         
15542         this.indicator = this.indicatorEl();
15543         
15544         this.store = Roo.factory(this.store, Roo.data);
15545         this.store.parent = this;
15546         
15547         // if we are building from html. then this element is so complex, that we can not really
15548         // use the rendered HTML.
15549         // so we have to trash and replace the previous code.
15550         if (Roo.XComponent.build_from_html) {
15551             // remove this element....
15552             var e = this.el.dom, k=0;
15553             while (e ) { e = e.previousSibling;  ++k;}
15554
15555             this.el.remove();
15556             
15557             this.el=false;
15558             this.rendered = false;
15559             
15560             this.render(this.parent().getChildContainer(true), k);
15561         }
15562         
15563         if(Roo.isIOS && this.useNativeIOS){
15564             this.initIOSView();
15565             return;
15566         }
15567         
15568         /*
15569          * Touch Devices
15570          */
15571         
15572         if(Roo.isTouch && this.mobileTouchView){
15573             this.initTouchView();
15574             return;
15575         }
15576         
15577         if(this.tickable){
15578             this.initTickableEvents();
15579             return;
15580         }
15581         
15582         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15583         
15584         if(this.hiddenName){
15585             
15586             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15587             
15588             this.hiddenField.dom.value =
15589                 this.hiddenValue !== undefined ? this.hiddenValue :
15590                 this.value !== undefined ? this.value : '';
15591
15592             // prevent input submission
15593             this.el.dom.removeAttribute('name');
15594             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15595              
15596              
15597         }
15598         //if(Roo.isGecko){
15599         //    this.el.dom.setAttribute('autocomplete', 'off');
15600         //}
15601         
15602         var cls = 'x-combo-list';
15603         
15604         //this.list = new Roo.Layer({
15605         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15606         //});
15607         
15608         var _this = this;
15609         
15610         (function(){
15611             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15612             _this.list.setWidth(lw);
15613         }).defer(100);
15614         
15615         this.list.on('mouseover', this.onViewOver, this);
15616         this.list.on('mousemove', this.onViewMove, this);
15617         this.list.on('scroll', this.onViewScroll, this);
15618         
15619         /*
15620         this.list.swallowEvent('mousewheel');
15621         this.assetHeight = 0;
15622
15623         if(this.title){
15624             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15625             this.assetHeight += this.header.getHeight();
15626         }
15627
15628         this.innerList = this.list.createChild({cls:cls+'-inner'});
15629         this.innerList.on('mouseover', this.onViewOver, this);
15630         this.innerList.on('mousemove', this.onViewMove, this);
15631         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15632         
15633         if(this.allowBlank && !this.pageSize && !this.disableClear){
15634             this.footer = this.list.createChild({cls:cls+'-ft'});
15635             this.pageTb = new Roo.Toolbar(this.footer);
15636            
15637         }
15638         if(this.pageSize){
15639             this.footer = this.list.createChild({cls:cls+'-ft'});
15640             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15641                     {pageSize: this.pageSize});
15642             
15643         }
15644         
15645         if (this.pageTb && this.allowBlank && !this.disableClear) {
15646             var _this = this;
15647             this.pageTb.add(new Roo.Toolbar.Fill(), {
15648                 cls: 'x-btn-icon x-btn-clear',
15649                 text: '&#160;',
15650                 handler: function()
15651                 {
15652                     _this.collapse();
15653                     _this.clearValue();
15654                     _this.onSelect(false, -1);
15655                 }
15656             });
15657         }
15658         if (this.footer) {
15659             this.assetHeight += this.footer.getHeight();
15660         }
15661         */
15662             
15663         if(!this.tpl){
15664             this.tpl = Roo.bootstrap.version == 4 ?
15665                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15666                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15667         }
15668
15669         this.view = new Roo.View(this.list, this.tpl, {
15670             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15671         });
15672         //this.view.wrapEl.setDisplayed(false);
15673         this.view.on('click', this.onViewClick, this);
15674         
15675         
15676         this.store.on('beforeload', this.onBeforeLoad, this);
15677         this.store.on('load', this.onLoad, this);
15678         this.store.on('loadexception', this.onLoadException, this);
15679         /*
15680         if(this.resizable){
15681             this.resizer = new Roo.Resizable(this.list,  {
15682                pinned:true, handles:'se'
15683             });
15684             this.resizer.on('resize', function(r, w, h){
15685                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15686                 this.listWidth = w;
15687                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15688                 this.restrictHeight();
15689             }, this);
15690             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15691         }
15692         */
15693         if(!this.editable){
15694             this.editable = true;
15695             this.setEditable(false);
15696         }
15697         
15698         /*
15699         
15700         if (typeof(this.events.add.listeners) != 'undefined') {
15701             
15702             this.addicon = this.wrap.createChild(
15703                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
15704        
15705             this.addicon.on('click', function(e) {
15706                 this.fireEvent('add', this);
15707             }, this);
15708         }
15709         if (typeof(this.events.edit.listeners) != 'undefined') {
15710             
15711             this.editicon = this.wrap.createChild(
15712                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
15713             if (this.addicon) {
15714                 this.editicon.setStyle('margin-left', '40px');
15715             }
15716             this.editicon.on('click', function(e) {
15717                 
15718                 // we fire even  if inothing is selected..
15719                 this.fireEvent('edit', this, this.lastData );
15720                 
15721             }, this);
15722         }
15723         */
15724         
15725         this.keyNav = new Roo.KeyNav(this.inputEl(), {
15726             "up" : function(e){
15727                 this.inKeyMode = true;
15728                 this.selectPrev();
15729             },
15730
15731             "down" : function(e){
15732                 if(!this.isExpanded()){
15733                     this.onTriggerClick();
15734                 }else{
15735                     this.inKeyMode = true;
15736                     this.selectNext();
15737                 }
15738             },
15739
15740             "enter" : function(e){
15741 //                this.onViewClick();
15742                 //return true;
15743                 this.collapse();
15744                 
15745                 if(this.fireEvent("specialkey", this, e)){
15746                     this.onViewClick(false);
15747                 }
15748                 
15749                 return true;
15750             },
15751
15752             "esc" : function(e){
15753                 this.collapse();
15754             },
15755
15756             "tab" : function(e){
15757                 this.collapse();
15758                 
15759                 if(this.fireEvent("specialkey", this, e)){
15760                     this.onViewClick(false);
15761                 }
15762                 
15763                 return true;
15764             },
15765
15766             scope : this,
15767
15768             doRelay : function(foo, bar, hname){
15769                 if(hname == 'down' || this.scope.isExpanded()){
15770                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15771                 }
15772                 return true;
15773             },
15774
15775             forceKeyDown: true
15776         });
15777         
15778         
15779         this.queryDelay = Math.max(this.queryDelay || 10,
15780                 this.mode == 'local' ? 10 : 250);
15781         
15782         
15783         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15784         
15785         if(this.typeAhead){
15786             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15787         }
15788         if(this.editable !== false){
15789             this.inputEl().on("keyup", this.onKeyUp, this);
15790         }
15791         if(this.forceSelection){
15792             this.inputEl().on('blur', this.doForce, this);
15793         }
15794         
15795         if(this.multiple){
15796             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15797             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15798         }
15799     },
15800     
15801     initTickableEvents: function()
15802     {   
15803         this.createList();
15804         
15805         if(this.hiddenName){
15806             
15807             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15808             
15809             this.hiddenField.dom.value =
15810                 this.hiddenValue !== undefined ? this.hiddenValue :
15811                 this.value !== undefined ? this.value : '';
15812
15813             // prevent input submission
15814             this.el.dom.removeAttribute('name');
15815             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15816              
15817              
15818         }
15819         
15820 //        this.list = this.el.select('ul.dropdown-menu',true).first();
15821         
15822         this.choices = this.el.select('ul.roo-select2-choices', true).first();
15823         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15824         if(this.triggerList){
15825             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15826         }
15827          
15828         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15829         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15830         
15831         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15832         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15833         
15834         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15835         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15836         
15837         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15838         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15839         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15840         
15841         this.okBtn.hide();
15842         this.cancelBtn.hide();
15843         
15844         var _this = this;
15845         
15846         (function(){
15847             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15848             _this.list.setWidth(lw);
15849         }).defer(100);
15850         
15851         this.list.on('mouseover', this.onViewOver, this);
15852         this.list.on('mousemove', this.onViewMove, this);
15853         
15854         this.list.on('scroll', this.onViewScroll, this);
15855         
15856         if(!this.tpl){
15857             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
15858                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15859         }
15860
15861         this.view = new Roo.View(this.list, this.tpl, {
15862             singleSelect:true,
15863             tickable:true,
15864             parent:this,
15865             store: this.store,
15866             selectedClass: this.selectedClass
15867         });
15868         
15869         //this.view.wrapEl.setDisplayed(false);
15870         this.view.on('click', this.onViewClick, this);
15871         
15872         
15873         
15874         this.store.on('beforeload', this.onBeforeLoad, this);
15875         this.store.on('load', this.onLoad, this);
15876         this.store.on('loadexception', this.onLoadException, this);
15877         
15878         if(this.editable){
15879             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15880                 "up" : function(e){
15881                     this.inKeyMode = true;
15882                     this.selectPrev();
15883                 },
15884
15885                 "down" : function(e){
15886                     this.inKeyMode = true;
15887                     this.selectNext();
15888                 },
15889
15890                 "enter" : function(e){
15891                     if(this.fireEvent("specialkey", this, e)){
15892                         this.onViewClick(false);
15893                     }
15894                     
15895                     return true;
15896                 },
15897
15898                 "esc" : function(e){
15899                     this.onTickableFooterButtonClick(e, false, false);
15900                 },
15901
15902                 "tab" : function(e){
15903                     this.fireEvent("specialkey", this, e);
15904                     
15905                     this.onTickableFooterButtonClick(e, false, false);
15906                     
15907                     return true;
15908                 },
15909
15910                 scope : this,
15911
15912                 doRelay : function(e, fn, key){
15913                     if(this.scope.isExpanded()){
15914                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15915                     }
15916                     return true;
15917                 },
15918
15919                 forceKeyDown: true
15920             });
15921         }
15922         
15923         this.queryDelay = Math.max(this.queryDelay || 10,
15924                 this.mode == 'local' ? 10 : 250);
15925         
15926         
15927         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15928         
15929         if(this.typeAhead){
15930             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15931         }
15932         
15933         if(this.editable !== false){
15934             this.tickableInputEl().on("keyup", this.onKeyUp, this);
15935         }
15936         
15937         this.indicator = this.indicatorEl();
15938         
15939         if(this.indicator){
15940             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15941             this.indicator.hide();
15942         }
15943         
15944     },
15945
15946     onDestroy : function(){
15947         if(this.view){
15948             this.view.setStore(null);
15949             this.view.el.removeAllListeners();
15950             this.view.el.remove();
15951             this.view.purgeListeners();
15952         }
15953         if(this.list){
15954             this.list.dom.innerHTML  = '';
15955         }
15956         
15957         if(this.store){
15958             this.store.un('beforeload', this.onBeforeLoad, this);
15959             this.store.un('load', this.onLoad, this);
15960             this.store.un('loadexception', this.onLoadException, this);
15961         }
15962         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15963     },
15964
15965     // private
15966     fireKey : function(e){
15967         if(e.isNavKeyPress() && !this.list.isVisible()){
15968             this.fireEvent("specialkey", this, e);
15969         }
15970     },
15971
15972     // private
15973     onResize: function(w, h)
15974     {
15975         
15976         
15977 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15978 //        
15979 //        if(typeof w != 'number'){
15980 //            // we do not handle it!?!?
15981 //            return;
15982 //        }
15983 //        var tw = this.trigger.getWidth();
15984 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
15985 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
15986 //        var x = w - tw;
15987 //        this.inputEl().setWidth( this.adjustWidth('input', x));
15988 //            
15989 //        //this.trigger.setStyle('left', x+'px');
15990 //        
15991 //        if(this.list && this.listWidth === undefined){
15992 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15993 //            this.list.setWidth(lw);
15994 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15995 //        }
15996         
15997     
15998         
15999     },
16000
16001     /**
16002      * Allow or prevent the user from directly editing the field text.  If false is passed,
16003      * the user will only be able to select from the items defined in the dropdown list.  This method
16004      * is the runtime equivalent of setting the 'editable' config option at config time.
16005      * @param {Boolean} value True to allow the user to directly edit the field text
16006      */
16007     setEditable : function(value){
16008         if(value == this.editable){
16009             return;
16010         }
16011         this.editable = value;
16012         if(!value){
16013             this.inputEl().dom.setAttribute('readOnly', true);
16014             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16015             this.inputEl().addClass('x-combo-noedit');
16016         }else{
16017             this.inputEl().dom.setAttribute('readOnly', false);
16018             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16019             this.inputEl().removeClass('x-combo-noedit');
16020         }
16021     },
16022
16023     // private
16024     
16025     onBeforeLoad : function(combo,opts){
16026         if(!this.hasFocus){
16027             return;
16028         }
16029          if (!opts.add) {
16030             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16031          }
16032         this.restrictHeight();
16033         this.selectedIndex = -1;
16034     },
16035
16036     // private
16037     onLoad : function(){
16038         
16039         this.hasQuery = false;
16040         
16041         if(!this.hasFocus){
16042             return;
16043         }
16044         
16045         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16046             this.loading.hide();
16047         }
16048         
16049         if(this.store.getCount() > 0){
16050             
16051             this.expand();
16052             this.restrictHeight();
16053             if(this.lastQuery == this.allQuery){
16054                 if(this.editable && !this.tickable){
16055                     this.inputEl().dom.select();
16056                 }
16057                 
16058                 if(
16059                     !this.selectByValue(this.value, true) &&
16060                     this.autoFocus && 
16061                     (
16062                         !this.store.lastOptions ||
16063                         typeof(this.store.lastOptions.add) == 'undefined' || 
16064                         this.store.lastOptions.add != true
16065                     )
16066                 ){
16067                     this.select(0, true);
16068                 }
16069             }else{
16070                 if(this.autoFocus){
16071                     this.selectNext();
16072                 }
16073                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16074                     this.taTask.delay(this.typeAheadDelay);
16075                 }
16076             }
16077         }else{
16078             this.onEmptyResults();
16079         }
16080         
16081         //this.el.focus();
16082     },
16083     // private
16084     onLoadException : function()
16085     {
16086         this.hasQuery = false;
16087         
16088         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16089             this.loading.hide();
16090         }
16091         
16092         if(this.tickable && this.editable){
16093             return;
16094         }
16095         
16096         this.collapse();
16097         // only causes errors at present
16098         //Roo.log(this.store.reader.jsonData);
16099         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16100             // fixme
16101             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16102         //}
16103         
16104         
16105     },
16106     // private
16107     onTypeAhead : function(){
16108         if(this.store.getCount() > 0){
16109             var r = this.store.getAt(0);
16110             var newValue = r.data[this.displayField];
16111             var len = newValue.length;
16112             var selStart = this.getRawValue().length;
16113             
16114             if(selStart != len){
16115                 this.setRawValue(newValue);
16116                 this.selectText(selStart, newValue.length);
16117             }
16118         }
16119     },
16120
16121     // private
16122     onSelect : function(record, index){
16123         
16124         if(this.fireEvent('beforeselect', this, record, index) !== false){
16125         
16126             this.setFromData(index > -1 ? record.data : false);
16127             
16128             this.collapse();
16129             this.fireEvent('select', this, record, index);
16130         }
16131     },
16132
16133     /**
16134      * Returns the currently selected field value or empty string if no value is set.
16135      * @return {String} value The selected value
16136      */
16137     getValue : function()
16138     {
16139         if(Roo.isIOS && this.useNativeIOS){
16140             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16141         }
16142         
16143         if(this.multiple){
16144             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16145         }
16146         
16147         if(this.valueField){
16148             return typeof this.value != 'undefined' ? this.value : '';
16149         }else{
16150             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16151         }
16152     },
16153     
16154     getRawValue : function()
16155     {
16156         if(Roo.isIOS && this.useNativeIOS){
16157             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16158         }
16159         
16160         var v = this.inputEl().getValue();
16161         
16162         return v;
16163     },
16164
16165     /**
16166      * Clears any text/value currently set in the field
16167      */
16168     clearValue : function(){
16169         
16170         if(this.hiddenField){
16171             this.hiddenField.dom.value = '';
16172         }
16173         this.value = '';
16174         this.setRawValue('');
16175         this.lastSelectionText = '';
16176         this.lastData = false;
16177         
16178         var close = this.closeTriggerEl();
16179         
16180         if(close){
16181             close.hide();
16182         }
16183         
16184         this.validate();
16185         
16186     },
16187
16188     /**
16189      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16190      * will be displayed in the field.  If the value does not match the data value of an existing item,
16191      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16192      * Otherwise the field will be blank (although the value will still be set).
16193      * @param {String} value The value to match
16194      */
16195     setValue : function(v)
16196     {
16197         if(Roo.isIOS && this.useNativeIOS){
16198             this.setIOSValue(v);
16199             return;
16200         }
16201         
16202         if(this.multiple){
16203             this.syncValue();
16204             return;
16205         }
16206         
16207         var text = v;
16208         if(this.valueField){
16209             var r = this.findRecord(this.valueField, v);
16210             if(r){
16211                 text = r.data[this.displayField];
16212             }else if(this.valueNotFoundText !== undefined){
16213                 text = this.valueNotFoundText;
16214             }
16215         }
16216         this.lastSelectionText = text;
16217         if(this.hiddenField){
16218             this.hiddenField.dom.value = v;
16219         }
16220         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16221         this.value = v;
16222         
16223         var close = this.closeTriggerEl();
16224         
16225         if(close){
16226             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16227         }
16228         
16229         this.validate();
16230     },
16231     /**
16232      * @property {Object} the last set data for the element
16233      */
16234     
16235     lastData : false,
16236     /**
16237      * Sets the value of the field based on a object which is related to the record format for the store.
16238      * @param {Object} value the value to set as. or false on reset?
16239      */
16240     setFromData : function(o){
16241         
16242         if(this.multiple){
16243             this.addItem(o);
16244             return;
16245         }
16246             
16247         var dv = ''; // display value
16248         var vv = ''; // value value..
16249         this.lastData = o;
16250         if (this.displayField) {
16251             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16252         } else {
16253             // this is an error condition!!!
16254             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16255         }
16256         
16257         if(this.valueField){
16258             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16259         }
16260         
16261         var close = this.closeTriggerEl();
16262         
16263         if(close){
16264             if(dv.length || vv * 1 > 0){
16265                 close.show() ;
16266                 this.blockFocus=true;
16267             } else {
16268                 close.hide();
16269             }             
16270         }
16271         
16272         if(this.hiddenField){
16273             this.hiddenField.dom.value = vv;
16274             
16275             this.lastSelectionText = dv;
16276             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16277             this.value = vv;
16278             return;
16279         }
16280         // no hidden field.. - we store the value in 'value', but still display
16281         // display field!!!!
16282         this.lastSelectionText = dv;
16283         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16284         this.value = vv;
16285         
16286         
16287         
16288     },
16289     // private
16290     reset : function(){
16291         // overridden so that last data is reset..
16292         
16293         if(this.multiple){
16294             this.clearItem();
16295             return;
16296         }
16297         
16298         this.setValue(this.originalValue);
16299         //this.clearInvalid();
16300         this.lastData = false;
16301         if (this.view) {
16302             this.view.clearSelections();
16303         }
16304         
16305         this.validate();
16306     },
16307     // private
16308     findRecord : function(prop, value){
16309         var record;
16310         if(this.store.getCount() > 0){
16311             this.store.each(function(r){
16312                 if(r.data[prop] == value){
16313                     record = r;
16314                     return false;
16315                 }
16316                 return true;
16317             });
16318         }
16319         return record;
16320     },
16321     
16322     getName: function()
16323     {
16324         // returns hidden if it's set..
16325         if (!this.rendered) {return ''};
16326         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16327         
16328     },
16329     // private
16330     onViewMove : function(e, t){
16331         this.inKeyMode = false;
16332     },
16333
16334     // private
16335     onViewOver : function(e, t){
16336         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16337             return;
16338         }
16339         var item = this.view.findItemFromChild(t);
16340         
16341         if(item){
16342             var index = this.view.indexOf(item);
16343             this.select(index, false);
16344         }
16345     },
16346
16347     // private
16348     onViewClick : function(view, doFocus, el, e)
16349     {
16350         var index = this.view.getSelectedIndexes()[0];
16351         
16352         var r = this.store.getAt(index);
16353         
16354         if(this.tickable){
16355             
16356             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16357                 return;
16358             }
16359             
16360             var rm = false;
16361             var _this = this;
16362             
16363             Roo.each(this.tickItems, function(v,k){
16364                 
16365                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16366                     Roo.log(v);
16367                     _this.tickItems.splice(k, 1);
16368                     
16369                     if(typeof(e) == 'undefined' && view == false){
16370                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16371                     }
16372                     
16373                     rm = true;
16374                     return;
16375                 }
16376             });
16377             
16378             if(rm){
16379                 return;
16380             }
16381             
16382             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16383                 this.tickItems.push(r.data);
16384             }
16385             
16386             if(typeof(e) == 'undefined' && view == false){
16387                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16388             }
16389                     
16390             return;
16391         }
16392         
16393         if(r){
16394             this.onSelect(r, index);
16395         }
16396         if(doFocus !== false && !this.blockFocus){
16397             this.inputEl().focus();
16398         }
16399     },
16400
16401     // private
16402     restrictHeight : function(){
16403         //this.innerList.dom.style.height = '';
16404         //var inner = this.innerList.dom;
16405         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16406         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16407         //this.list.beginUpdate();
16408         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16409         this.list.alignTo(this.inputEl(), this.listAlign);
16410         this.list.alignTo(this.inputEl(), this.listAlign);
16411         //this.list.endUpdate();
16412     },
16413
16414     // private
16415     onEmptyResults : function(){
16416         
16417         if(this.tickable && this.editable){
16418             this.hasFocus = false;
16419             this.restrictHeight();
16420             return;
16421         }
16422         
16423         this.collapse();
16424     },
16425
16426     /**
16427      * Returns true if the dropdown list is expanded, else false.
16428      */
16429     isExpanded : function(){
16430         return this.list.isVisible();
16431     },
16432
16433     /**
16434      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16435      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16436      * @param {String} value The data value of the item to select
16437      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16438      * selected item if it is not currently in view (defaults to true)
16439      * @return {Boolean} True if the value matched an item in the list, else false
16440      */
16441     selectByValue : function(v, scrollIntoView){
16442         if(v !== undefined && v !== null){
16443             var r = this.findRecord(this.valueField || this.displayField, v);
16444             if(r){
16445                 this.select(this.store.indexOf(r), scrollIntoView);
16446                 return true;
16447             }
16448         }
16449         return false;
16450     },
16451
16452     /**
16453      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16454      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16455      * @param {Number} index The zero-based index of the list item to select
16456      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16457      * selected item if it is not currently in view (defaults to true)
16458      */
16459     select : function(index, scrollIntoView){
16460         this.selectedIndex = index;
16461         this.view.select(index);
16462         if(scrollIntoView !== false){
16463             var el = this.view.getNode(index);
16464             /*
16465              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16466              */
16467             if(el){
16468                 this.list.scrollChildIntoView(el, false);
16469             }
16470         }
16471     },
16472
16473     // private
16474     selectNext : function(){
16475         var ct = this.store.getCount();
16476         if(ct > 0){
16477             if(this.selectedIndex == -1){
16478                 this.select(0);
16479             }else if(this.selectedIndex < ct-1){
16480                 this.select(this.selectedIndex+1);
16481             }
16482         }
16483     },
16484
16485     // private
16486     selectPrev : function(){
16487         var ct = this.store.getCount();
16488         if(ct > 0){
16489             if(this.selectedIndex == -1){
16490                 this.select(0);
16491             }else if(this.selectedIndex != 0){
16492                 this.select(this.selectedIndex-1);
16493             }
16494         }
16495     },
16496
16497     // private
16498     onKeyUp : function(e){
16499         if(this.editable !== false && !e.isSpecialKey()){
16500             this.lastKey = e.getKey();
16501             this.dqTask.delay(this.queryDelay);
16502         }
16503     },
16504
16505     // private
16506     validateBlur : function(){
16507         return !this.list || !this.list.isVisible();   
16508     },
16509
16510     // private
16511     initQuery : function(){
16512         
16513         var v = this.getRawValue();
16514         
16515         if(this.tickable && this.editable){
16516             v = this.tickableInputEl().getValue();
16517         }
16518         
16519         this.doQuery(v);
16520     },
16521
16522     // private
16523     doForce : function(){
16524         if(this.inputEl().dom.value.length > 0){
16525             this.inputEl().dom.value =
16526                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16527              
16528         }
16529     },
16530
16531     /**
16532      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16533      * query allowing the query action to be canceled if needed.
16534      * @param {String} query The SQL query to execute
16535      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16536      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16537      * saved in the current store (defaults to false)
16538      */
16539     doQuery : function(q, forceAll){
16540         
16541         if(q === undefined || q === null){
16542             q = '';
16543         }
16544         var qe = {
16545             query: q,
16546             forceAll: forceAll,
16547             combo: this,
16548             cancel:false
16549         };
16550         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16551             return false;
16552         }
16553         q = qe.query;
16554         
16555         forceAll = qe.forceAll;
16556         if(forceAll === true || (q.length >= this.minChars)){
16557             
16558             this.hasQuery = true;
16559             
16560             if(this.lastQuery != q || this.alwaysQuery){
16561                 this.lastQuery = q;
16562                 if(this.mode == 'local'){
16563                     this.selectedIndex = -1;
16564                     if(forceAll){
16565                         this.store.clearFilter();
16566                     }else{
16567                         
16568                         if(this.specialFilter){
16569                             this.fireEvent('specialfilter', this);
16570                             this.onLoad();
16571                             return;
16572                         }
16573                         
16574                         this.store.filter(this.displayField, q);
16575                     }
16576                     
16577                     this.store.fireEvent("datachanged", this.store);
16578                     
16579                     this.onLoad();
16580                     
16581                     
16582                 }else{
16583                     
16584                     this.store.baseParams[this.queryParam] = q;
16585                     
16586                     var options = {params : this.getParams(q)};
16587                     
16588                     if(this.loadNext){
16589                         options.add = true;
16590                         options.params.start = this.page * this.pageSize;
16591                     }
16592                     
16593                     this.store.load(options);
16594                     
16595                     /*
16596                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16597                      *  we should expand the list on onLoad
16598                      *  so command out it
16599                      */
16600 //                    this.expand();
16601                 }
16602             }else{
16603                 this.selectedIndex = -1;
16604                 this.onLoad();   
16605             }
16606         }
16607         
16608         this.loadNext = false;
16609     },
16610     
16611     // private
16612     getParams : function(q){
16613         var p = {};
16614         //p[this.queryParam] = q;
16615         
16616         if(this.pageSize){
16617             p.start = 0;
16618             p.limit = this.pageSize;
16619         }
16620         return p;
16621     },
16622
16623     /**
16624      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16625      */
16626     collapse : function(){
16627         if(!this.isExpanded()){
16628             return;
16629         }
16630         
16631         this.list.hide();
16632         
16633         this.hasFocus = false;
16634         
16635         if(this.tickable){
16636             this.okBtn.hide();
16637             this.cancelBtn.hide();
16638             this.trigger.show();
16639             
16640             if(this.editable){
16641                 this.tickableInputEl().dom.value = '';
16642                 this.tickableInputEl().blur();
16643             }
16644             
16645         }
16646         
16647         Roo.get(document).un('mousedown', this.collapseIf, this);
16648         Roo.get(document).un('mousewheel', this.collapseIf, this);
16649         if (!this.editable) {
16650             Roo.get(document).un('keydown', this.listKeyPress, this);
16651         }
16652         this.fireEvent('collapse', this);
16653         
16654         this.validate();
16655     },
16656
16657     // private
16658     collapseIf : function(e){
16659         var in_combo  = e.within(this.el);
16660         var in_list =  e.within(this.list);
16661         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16662         
16663         if (in_combo || in_list || is_list) {
16664             //e.stopPropagation();
16665             return;
16666         }
16667         
16668         if(this.tickable){
16669             this.onTickableFooterButtonClick(e, false, false);
16670         }
16671
16672         this.collapse();
16673         
16674     },
16675
16676     /**
16677      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16678      */
16679     expand : function(){
16680        
16681         if(this.isExpanded() || !this.hasFocus){
16682             return;
16683         }
16684         
16685         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16686         this.list.setWidth(lw);
16687         
16688         Roo.log('expand');
16689         
16690         this.list.show();
16691         
16692         this.restrictHeight();
16693         
16694         if(this.tickable){
16695             
16696             this.tickItems = Roo.apply([], this.item);
16697             
16698             this.okBtn.show();
16699             this.cancelBtn.show();
16700             this.trigger.hide();
16701             
16702             if(this.editable){
16703                 this.tickableInputEl().focus();
16704             }
16705             
16706         }
16707         
16708         Roo.get(document).on('mousedown', this.collapseIf, this);
16709         Roo.get(document).on('mousewheel', this.collapseIf, this);
16710         if (!this.editable) {
16711             Roo.get(document).on('keydown', this.listKeyPress, this);
16712         }
16713         
16714         this.fireEvent('expand', this);
16715     },
16716
16717     // private
16718     // Implements the default empty TriggerField.onTriggerClick function
16719     onTriggerClick : function(e)
16720     {
16721         Roo.log('trigger click');
16722         
16723         if(this.disabled || !this.triggerList){
16724             return;
16725         }
16726         
16727         this.page = 0;
16728         this.loadNext = false;
16729         
16730         if(this.isExpanded()){
16731             this.collapse();
16732             if (!this.blockFocus) {
16733                 this.inputEl().focus();
16734             }
16735             
16736         }else {
16737             this.hasFocus = true;
16738             if(this.triggerAction == 'all') {
16739                 this.doQuery(this.allQuery, true);
16740             } else {
16741                 this.doQuery(this.getRawValue());
16742             }
16743             if (!this.blockFocus) {
16744                 this.inputEl().focus();
16745             }
16746         }
16747     },
16748     
16749     onTickableTriggerClick : function(e)
16750     {
16751         if(this.disabled){
16752             return;
16753         }
16754         
16755         this.page = 0;
16756         this.loadNext = false;
16757         this.hasFocus = true;
16758         
16759         if(this.triggerAction == 'all') {
16760             this.doQuery(this.allQuery, true);
16761         } else {
16762             this.doQuery(this.getRawValue());
16763         }
16764     },
16765     
16766     onSearchFieldClick : function(e)
16767     {
16768         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16769             this.onTickableFooterButtonClick(e, false, false);
16770             return;
16771         }
16772         
16773         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16774             return;
16775         }
16776         
16777         this.page = 0;
16778         this.loadNext = false;
16779         this.hasFocus = true;
16780         
16781         if(this.triggerAction == 'all') {
16782             this.doQuery(this.allQuery, true);
16783         } else {
16784             this.doQuery(this.getRawValue());
16785         }
16786     },
16787     
16788     listKeyPress : function(e)
16789     {
16790         //Roo.log('listkeypress');
16791         // scroll to first matching element based on key pres..
16792         if (e.isSpecialKey()) {
16793             return false;
16794         }
16795         var k = String.fromCharCode(e.getKey()).toUpperCase();
16796         //Roo.log(k);
16797         var match  = false;
16798         var csel = this.view.getSelectedNodes();
16799         var cselitem = false;
16800         if (csel.length) {
16801             var ix = this.view.indexOf(csel[0]);
16802             cselitem  = this.store.getAt(ix);
16803             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16804                 cselitem = false;
16805             }
16806             
16807         }
16808         
16809         this.store.each(function(v) { 
16810             if (cselitem) {
16811                 // start at existing selection.
16812                 if (cselitem.id == v.id) {
16813                     cselitem = false;
16814                 }
16815                 return true;
16816             }
16817                 
16818             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16819                 match = this.store.indexOf(v);
16820                 return false;
16821             }
16822             return true;
16823         }, this);
16824         
16825         if (match === false) {
16826             return true; // no more action?
16827         }
16828         // scroll to?
16829         this.view.select(match);
16830         var sn = Roo.get(this.view.getSelectedNodes()[0]);
16831         sn.scrollIntoView(sn.dom.parentNode, false);
16832     },
16833     
16834     onViewScroll : function(e, t){
16835         
16836         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){
16837             return;
16838         }
16839         
16840         this.hasQuery = true;
16841         
16842         this.loading = this.list.select('.loading', true).first();
16843         
16844         if(this.loading === null){
16845             this.list.createChild({
16846                 tag: 'div',
16847                 cls: 'loading roo-select2-more-results roo-select2-active',
16848                 html: 'Loading more results...'
16849             });
16850             
16851             this.loading = this.list.select('.loading', true).first();
16852             
16853             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16854             
16855             this.loading.hide();
16856         }
16857         
16858         this.loading.show();
16859         
16860         var _combo = this;
16861         
16862         this.page++;
16863         this.loadNext = true;
16864         
16865         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16866         
16867         return;
16868     },
16869     
16870     addItem : function(o)
16871     {   
16872         var dv = ''; // display value
16873         
16874         if (this.displayField) {
16875             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16876         } else {
16877             // this is an error condition!!!
16878             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16879         }
16880         
16881         if(!dv.length){
16882             return;
16883         }
16884         
16885         var choice = this.choices.createChild({
16886             tag: 'li',
16887             cls: 'roo-select2-search-choice',
16888             cn: [
16889                 {
16890                     tag: 'div',
16891                     html: dv
16892                 },
16893                 {
16894                     tag: 'a',
16895                     href: '#',
16896                     cls: 'roo-select2-search-choice-close fa fa-times',
16897                     tabindex: '-1'
16898                 }
16899             ]
16900             
16901         }, this.searchField);
16902         
16903         var close = choice.select('a.roo-select2-search-choice-close', true).first();
16904         
16905         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16906         
16907         this.item.push(o);
16908         
16909         this.lastData = o;
16910         
16911         this.syncValue();
16912         
16913         this.inputEl().dom.value = '';
16914         
16915         this.validate();
16916     },
16917     
16918     onRemoveItem : function(e, _self, o)
16919     {
16920         e.preventDefault();
16921         
16922         this.lastItem = Roo.apply([], this.item);
16923         
16924         var index = this.item.indexOf(o.data) * 1;
16925         
16926         if( index < 0){
16927             Roo.log('not this item?!');
16928             return;
16929         }
16930         
16931         this.item.splice(index, 1);
16932         o.item.remove();
16933         
16934         this.syncValue();
16935         
16936         this.fireEvent('remove', this, e);
16937         
16938         this.validate();
16939         
16940     },
16941     
16942     syncValue : function()
16943     {
16944         if(!this.item.length){
16945             this.clearValue();
16946             return;
16947         }
16948             
16949         var value = [];
16950         var _this = this;
16951         Roo.each(this.item, function(i){
16952             if(_this.valueField){
16953                 value.push(i[_this.valueField]);
16954                 return;
16955             }
16956
16957             value.push(i);
16958         });
16959
16960         this.value = value.join(',');
16961
16962         if(this.hiddenField){
16963             this.hiddenField.dom.value = this.value;
16964         }
16965         
16966         this.store.fireEvent("datachanged", this.store);
16967         
16968         this.validate();
16969     },
16970     
16971     clearItem : function()
16972     {
16973         if(!this.multiple){
16974             return;
16975         }
16976         
16977         this.item = [];
16978         
16979         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16980            c.remove();
16981         });
16982         
16983         this.syncValue();
16984         
16985         this.validate();
16986         
16987         if(this.tickable && !Roo.isTouch){
16988             this.view.refresh();
16989         }
16990     },
16991     
16992     inputEl: function ()
16993     {
16994         if(Roo.isIOS && this.useNativeIOS){
16995             return this.el.select('select.roo-ios-select', true).first();
16996         }
16997         
16998         if(Roo.isTouch && this.mobileTouchView){
16999             return this.el.select('input.form-control',true).first();
17000         }
17001         
17002         if(this.tickable){
17003             return this.searchField;
17004         }
17005         
17006         return this.el.select('input.form-control',true).first();
17007     },
17008     
17009     onTickableFooterButtonClick : function(e, btn, el)
17010     {
17011         e.preventDefault();
17012         
17013         this.lastItem = Roo.apply([], this.item);
17014         
17015         if(btn && btn.name == 'cancel'){
17016             this.tickItems = Roo.apply([], this.item);
17017             this.collapse();
17018             return;
17019         }
17020         
17021         this.clearItem();
17022         
17023         var _this = this;
17024         
17025         Roo.each(this.tickItems, function(o){
17026             _this.addItem(o);
17027         });
17028         
17029         this.collapse();
17030         
17031     },
17032     
17033     validate : function()
17034     {
17035         if(this.getVisibilityEl().hasClass('hidden')){
17036             return true;
17037         }
17038         
17039         var v = this.getRawValue();
17040         
17041         if(this.multiple){
17042             v = this.getValue();
17043         }
17044         
17045         if(this.disabled || this.allowBlank || v.length){
17046             this.markValid();
17047             return true;
17048         }
17049         
17050         this.markInvalid();
17051         return false;
17052     },
17053     
17054     tickableInputEl : function()
17055     {
17056         if(!this.tickable || !this.editable){
17057             return this.inputEl();
17058         }
17059         
17060         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17061     },
17062     
17063     
17064     getAutoCreateTouchView : function()
17065     {
17066         var id = Roo.id();
17067         
17068         var cfg = {
17069             cls: 'form-group' //input-group
17070         };
17071         
17072         var input =  {
17073             tag: 'input',
17074             id : id,
17075             type : this.inputType,
17076             cls : 'form-control x-combo-noedit',
17077             autocomplete: 'new-password',
17078             placeholder : this.placeholder || '',
17079             readonly : true
17080         };
17081         
17082         if (this.name) {
17083             input.name = this.name;
17084         }
17085         
17086         if (this.size) {
17087             input.cls += ' input-' + this.size;
17088         }
17089         
17090         if (this.disabled) {
17091             input.disabled = true;
17092         }
17093         
17094         var inputblock = {
17095             cls : 'roo-combobox-wrap',
17096             cn : [
17097                 input
17098             ]
17099         };
17100         
17101         if(this.before){
17102             inputblock.cls += ' input-group';
17103             
17104             inputblock.cn.unshift({
17105                 tag :'span',
17106                 cls : 'input-group-addon input-group-prepend input-group-text',
17107                 html : this.before
17108             });
17109         }
17110         
17111         if(this.removable && !this.multiple){
17112             inputblock.cls += ' roo-removable';
17113             
17114             inputblock.cn.push({
17115                 tag: 'button',
17116                 html : 'x',
17117                 cls : 'roo-combo-removable-btn close'
17118             });
17119         }
17120
17121         if(this.hasFeedback && !this.allowBlank){
17122             
17123             inputblock.cls += ' has-feedback';
17124             
17125             inputblock.cn.push({
17126                 tag: 'span',
17127                 cls: 'glyphicon form-control-feedback'
17128             });
17129             
17130         }
17131         
17132         if (this.after) {
17133             
17134             inputblock.cls += (this.before) ? '' : ' input-group';
17135             
17136             inputblock.cn.push({
17137                 tag :'span',
17138                 cls : 'input-group-addon input-group-append input-group-text',
17139                 html : this.after
17140             });
17141         }
17142
17143         
17144         var ibwrap = inputblock;
17145         
17146         if(this.multiple){
17147             ibwrap = {
17148                 tag: 'ul',
17149                 cls: 'roo-select2-choices',
17150                 cn:[
17151                     {
17152                         tag: 'li',
17153                         cls: 'roo-select2-search-field',
17154                         cn: [
17155
17156                             inputblock
17157                         ]
17158                     }
17159                 ]
17160             };
17161         
17162             
17163         }
17164         
17165         var combobox = {
17166             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17167             cn: [
17168                 {
17169                     tag: 'input',
17170                     type : 'hidden',
17171                     cls: 'form-hidden-field'
17172                 },
17173                 ibwrap
17174             ]
17175         };
17176         
17177         if(!this.multiple && this.showToggleBtn){
17178             
17179             var caret = {
17180                 cls: 'caret'
17181             };
17182             
17183             if (this.caret != false) {
17184                 caret = {
17185                      tag: 'i',
17186                      cls: 'fa fa-' + this.caret
17187                 };
17188                 
17189             }
17190             
17191             combobox.cn.push({
17192                 tag :'span',
17193                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17194                 cn : [
17195                     Roo.bootstrap.version == 3 ? caret : '',
17196                     {
17197                         tag: 'span',
17198                         cls: 'combobox-clear',
17199                         cn  : [
17200                             {
17201                                 tag : 'i',
17202                                 cls: 'icon-remove'
17203                             }
17204                         ]
17205                     }
17206                 ]
17207
17208             })
17209         }
17210         
17211         if(this.multiple){
17212             combobox.cls += ' roo-select2-container-multi';
17213         }
17214         
17215         var align = this.labelAlign || this.parentLabelAlign();
17216         
17217         if (align ==='left' && this.fieldLabel.length) {
17218
17219             cfg.cn = [
17220                 {
17221                    tag : 'i',
17222                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17223                    tooltip : 'This field is required'
17224                 },
17225                 {
17226                     tag: 'label',
17227                     cls : 'control-label col-form-label',
17228                     html : this.fieldLabel
17229
17230                 },
17231                 {
17232                     cls : 'roo-combobox-wrap ', 
17233                     cn: [
17234                         combobox
17235                     ]
17236                 }
17237             ];
17238             
17239             var labelCfg = cfg.cn[1];
17240             var contentCfg = cfg.cn[2];
17241             
17242
17243             if(this.indicatorpos == 'right'){
17244                 cfg.cn = [
17245                     {
17246                         tag: 'label',
17247                         'for' :  id,
17248                         cls : 'control-label col-form-label',
17249                         cn : [
17250                             {
17251                                 tag : 'span',
17252                                 html : this.fieldLabel
17253                             },
17254                             {
17255                                 tag : 'i',
17256                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17257                                 tooltip : 'This field is required'
17258                             }
17259                         ]
17260                     },
17261                     {
17262                         cls : "roo-combobox-wrap ",
17263                         cn: [
17264                             combobox
17265                         ]
17266                     }
17267
17268                 ];
17269                 
17270                 labelCfg = cfg.cn[0];
17271                 contentCfg = cfg.cn[1];
17272             }
17273             
17274            
17275             
17276             if(this.labelWidth > 12){
17277                 labelCfg.style = "width: " + this.labelWidth + 'px';
17278             }
17279            
17280             if(this.labelWidth < 13 && this.labelmd == 0){
17281                 this.labelmd = this.labelWidth;
17282             }
17283             
17284             if(this.labellg > 0){
17285                 labelCfg.cls += ' col-lg-' + this.labellg;
17286                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17287             }
17288             
17289             if(this.labelmd > 0){
17290                 labelCfg.cls += ' col-md-' + this.labelmd;
17291                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17292             }
17293             
17294             if(this.labelsm > 0){
17295                 labelCfg.cls += ' col-sm-' + this.labelsm;
17296                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17297             }
17298             
17299             if(this.labelxs > 0){
17300                 labelCfg.cls += ' col-xs-' + this.labelxs;
17301                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17302             }
17303                 
17304                 
17305         } else if ( this.fieldLabel.length) {
17306             cfg.cn = [
17307                 {
17308                    tag : 'i',
17309                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17310                    tooltip : 'This field is required'
17311                 },
17312                 {
17313                     tag: 'label',
17314                     cls : 'control-label',
17315                     html : this.fieldLabel
17316
17317                 },
17318                 {
17319                     cls : '', 
17320                     cn: [
17321                         combobox
17322                     ]
17323                 }
17324             ];
17325             
17326             if(this.indicatorpos == 'right'){
17327                 cfg.cn = [
17328                     {
17329                         tag: 'label',
17330                         cls : 'control-label',
17331                         html : this.fieldLabel,
17332                         cn : [
17333                             {
17334                                tag : 'i',
17335                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17336                                tooltip : 'This field is required'
17337                             }
17338                         ]
17339                     },
17340                     {
17341                         cls : '', 
17342                         cn: [
17343                             combobox
17344                         ]
17345                     }
17346                 ];
17347             }
17348         } else {
17349             cfg.cn = combobox;    
17350         }
17351         
17352         
17353         var settings = this;
17354         
17355         ['xs','sm','md','lg'].map(function(size){
17356             if (settings[size]) {
17357                 cfg.cls += ' col-' + size + '-' + settings[size];
17358             }
17359         });
17360         
17361         return cfg;
17362     },
17363     
17364     initTouchView : function()
17365     {
17366         this.renderTouchView();
17367         
17368         this.touchViewEl.on('scroll', function(){
17369             this.el.dom.scrollTop = 0;
17370         }, this);
17371         
17372         this.originalValue = this.getValue();
17373         
17374         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17375         
17376         this.inputEl().on("click", this.showTouchView, this);
17377         if (this.triggerEl) {
17378             this.triggerEl.on("click", this.showTouchView, this);
17379         }
17380         
17381         
17382         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17383         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17384         
17385         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17386         
17387         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17388         this.store.on('load', this.onTouchViewLoad, this);
17389         this.store.on('loadexception', this.onTouchViewLoadException, this);
17390         
17391         if(this.hiddenName){
17392             
17393             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17394             
17395             this.hiddenField.dom.value =
17396                 this.hiddenValue !== undefined ? this.hiddenValue :
17397                 this.value !== undefined ? this.value : '';
17398         
17399             this.el.dom.removeAttribute('name');
17400             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17401         }
17402         
17403         if(this.multiple){
17404             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17405             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17406         }
17407         
17408         if(this.removable && !this.multiple){
17409             var close = this.closeTriggerEl();
17410             if(close){
17411                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17412                 close.on('click', this.removeBtnClick, this, close);
17413             }
17414         }
17415         /*
17416          * fix the bug in Safari iOS8
17417          */
17418         this.inputEl().on("focus", function(e){
17419             document.activeElement.blur();
17420         }, this);
17421         
17422         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17423         
17424         return;
17425         
17426         
17427     },
17428     
17429     renderTouchView : function()
17430     {
17431         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17432         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17433         
17434         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17435         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17436         
17437         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17438         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17439         this.touchViewBodyEl.setStyle('overflow', 'auto');
17440         
17441         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17442         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17443         
17444         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17445         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17446         
17447     },
17448     
17449     showTouchView : function()
17450     {
17451         if(this.disabled){
17452             return;
17453         }
17454         
17455         this.touchViewHeaderEl.hide();
17456
17457         if(this.modalTitle.length){
17458             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17459             this.touchViewHeaderEl.show();
17460         }
17461
17462         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17463         this.touchViewEl.show();
17464
17465         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17466         
17467         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17468         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17469
17470         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17471
17472         if(this.modalTitle.length){
17473             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17474         }
17475         
17476         this.touchViewBodyEl.setHeight(bodyHeight);
17477
17478         if(this.animate){
17479             var _this = this;
17480             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17481         }else{
17482             this.touchViewEl.addClass(['in','show']);
17483         }
17484         
17485         if(this._touchViewMask){
17486             Roo.get(document.body).addClass("x-body-masked");
17487             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17488             this._touchViewMask.setStyle('z-index', 10000);
17489             this._touchViewMask.addClass('show');
17490         }
17491         
17492         this.doTouchViewQuery();
17493         
17494     },
17495     
17496     hideTouchView : function()
17497     {
17498         this.touchViewEl.removeClass(['in','show']);
17499
17500         if(this.animate){
17501             var _this = this;
17502             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17503         }else{
17504             this.touchViewEl.setStyle('display', 'none');
17505         }
17506         
17507         if(this._touchViewMask){
17508             this._touchViewMask.removeClass('show');
17509             Roo.get(document.body).removeClass("x-body-masked");
17510         }
17511     },
17512     
17513     setTouchViewValue : function()
17514     {
17515         if(this.multiple){
17516             this.clearItem();
17517         
17518             var _this = this;
17519
17520             Roo.each(this.tickItems, function(o){
17521                 this.addItem(o);
17522             }, this);
17523         }
17524         
17525         this.hideTouchView();
17526     },
17527     
17528     doTouchViewQuery : function()
17529     {
17530         var qe = {
17531             query: '',
17532             forceAll: true,
17533             combo: this,
17534             cancel:false
17535         };
17536         
17537         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17538             return false;
17539         }
17540         
17541         if(!this.alwaysQuery || this.mode == 'local'){
17542             this.onTouchViewLoad();
17543             return;
17544         }
17545         
17546         this.store.load();
17547     },
17548     
17549     onTouchViewBeforeLoad : function(combo,opts)
17550     {
17551         return;
17552     },
17553
17554     // private
17555     onTouchViewLoad : function()
17556     {
17557         if(this.store.getCount() < 1){
17558             this.onTouchViewEmptyResults();
17559             return;
17560         }
17561         
17562         this.clearTouchView();
17563         
17564         var rawValue = this.getRawValue();
17565         
17566         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17567         
17568         this.tickItems = [];
17569         
17570         this.store.data.each(function(d, rowIndex){
17571             var row = this.touchViewListGroup.createChild(template);
17572             
17573             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17574                 row.addClass(d.data.cls);
17575             }
17576             
17577             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17578                 var cfg = {
17579                     data : d.data,
17580                     html : d.data[this.displayField]
17581                 };
17582                 
17583                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17584                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17585                 }
17586             }
17587             row.removeClass('selected');
17588             if(!this.multiple && this.valueField &&
17589                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17590             {
17591                 // radio buttons..
17592                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17593                 row.addClass('selected');
17594             }
17595             
17596             if(this.multiple && this.valueField &&
17597                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17598             {
17599                 
17600                 // checkboxes...
17601                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17602                 this.tickItems.push(d.data);
17603             }
17604             
17605             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17606             
17607         }, this);
17608         
17609         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17610         
17611         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17612
17613         if(this.modalTitle.length){
17614             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17615         }
17616
17617         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17618         
17619         if(this.mobile_restrict_height && listHeight < bodyHeight){
17620             this.touchViewBodyEl.setHeight(listHeight);
17621         }
17622         
17623         var _this = this;
17624         
17625         if(firstChecked && listHeight > bodyHeight){
17626             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17627         }
17628         
17629     },
17630     
17631     onTouchViewLoadException : function()
17632     {
17633         this.hideTouchView();
17634     },
17635     
17636     onTouchViewEmptyResults : function()
17637     {
17638         this.clearTouchView();
17639         
17640         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17641         
17642         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17643         
17644     },
17645     
17646     clearTouchView : function()
17647     {
17648         this.touchViewListGroup.dom.innerHTML = '';
17649     },
17650     
17651     onTouchViewClick : function(e, el, o)
17652     {
17653         e.preventDefault();
17654         
17655         var row = o.row;
17656         var rowIndex = o.rowIndex;
17657         
17658         var r = this.store.getAt(rowIndex);
17659         
17660         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17661             
17662             if(!this.multiple){
17663                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17664                     c.dom.removeAttribute('checked');
17665                 }, this);
17666
17667                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17668
17669                 this.setFromData(r.data);
17670
17671                 var close = this.closeTriggerEl();
17672
17673                 if(close){
17674                     close.show();
17675                 }
17676
17677                 this.hideTouchView();
17678
17679                 this.fireEvent('select', this, r, rowIndex);
17680
17681                 return;
17682             }
17683
17684             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17685                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17686                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17687                 return;
17688             }
17689
17690             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17691             this.addItem(r.data);
17692             this.tickItems.push(r.data);
17693         }
17694     },
17695     
17696     getAutoCreateNativeIOS : function()
17697     {
17698         var cfg = {
17699             cls: 'form-group' //input-group,
17700         };
17701         
17702         var combobox =  {
17703             tag: 'select',
17704             cls : 'roo-ios-select'
17705         };
17706         
17707         if (this.name) {
17708             combobox.name = this.name;
17709         }
17710         
17711         if (this.disabled) {
17712             combobox.disabled = true;
17713         }
17714         
17715         var settings = this;
17716         
17717         ['xs','sm','md','lg'].map(function(size){
17718             if (settings[size]) {
17719                 cfg.cls += ' col-' + size + '-' + settings[size];
17720             }
17721         });
17722         
17723         cfg.cn = combobox;
17724         
17725         return cfg;
17726         
17727     },
17728     
17729     initIOSView : function()
17730     {
17731         this.store.on('load', this.onIOSViewLoad, this);
17732         
17733         return;
17734     },
17735     
17736     onIOSViewLoad : function()
17737     {
17738         if(this.store.getCount() < 1){
17739             return;
17740         }
17741         
17742         this.clearIOSView();
17743         
17744         if(this.allowBlank) {
17745             
17746             var default_text = '-- SELECT --';
17747             
17748             if(this.placeholder.length){
17749                 default_text = this.placeholder;
17750             }
17751             
17752             if(this.emptyTitle.length){
17753                 default_text += ' - ' + this.emptyTitle + ' -';
17754             }
17755             
17756             var opt = this.inputEl().createChild({
17757                 tag: 'option',
17758                 value : 0,
17759                 html : default_text
17760             });
17761             
17762             var o = {};
17763             o[this.valueField] = 0;
17764             o[this.displayField] = default_text;
17765             
17766             this.ios_options.push({
17767                 data : o,
17768                 el : opt
17769             });
17770             
17771         }
17772         
17773         this.store.data.each(function(d, rowIndex){
17774             
17775             var html = '';
17776             
17777             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17778                 html = d.data[this.displayField];
17779             }
17780             
17781             var value = '';
17782             
17783             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17784                 value = d.data[this.valueField];
17785             }
17786             
17787             var option = {
17788                 tag: 'option',
17789                 value : value,
17790                 html : html
17791             };
17792             
17793             if(this.value == d.data[this.valueField]){
17794                 option['selected'] = true;
17795             }
17796             
17797             var opt = this.inputEl().createChild(option);
17798             
17799             this.ios_options.push({
17800                 data : d.data,
17801                 el : opt
17802             });
17803             
17804         }, this);
17805         
17806         this.inputEl().on('change', function(){
17807            this.fireEvent('select', this);
17808         }, this);
17809         
17810     },
17811     
17812     clearIOSView: function()
17813     {
17814         this.inputEl().dom.innerHTML = '';
17815         
17816         this.ios_options = [];
17817     },
17818     
17819     setIOSValue: function(v)
17820     {
17821         this.value = v;
17822         
17823         if(!this.ios_options){
17824             return;
17825         }
17826         
17827         Roo.each(this.ios_options, function(opts){
17828            
17829            opts.el.dom.removeAttribute('selected');
17830            
17831            if(opts.data[this.valueField] != v){
17832                return;
17833            }
17834            
17835            opts.el.dom.setAttribute('selected', true);
17836            
17837         }, this);
17838     }
17839
17840     /** 
17841     * @cfg {Boolean} grow 
17842     * @hide 
17843     */
17844     /** 
17845     * @cfg {Number} growMin 
17846     * @hide 
17847     */
17848     /** 
17849     * @cfg {Number} growMax 
17850     * @hide 
17851     */
17852     /**
17853      * @hide
17854      * @method autoSize
17855      */
17856 });
17857
17858 Roo.apply(Roo.bootstrap.ComboBox,  {
17859     
17860     header : {
17861         tag: 'div',
17862         cls: 'modal-header',
17863         cn: [
17864             {
17865                 tag: 'h4',
17866                 cls: 'modal-title'
17867             }
17868         ]
17869     },
17870     
17871     body : {
17872         tag: 'div',
17873         cls: 'modal-body',
17874         cn: [
17875             {
17876                 tag: 'ul',
17877                 cls: 'list-group'
17878             }
17879         ]
17880     },
17881     
17882     listItemRadio : {
17883         tag: 'li',
17884         cls: 'list-group-item',
17885         cn: [
17886             {
17887                 tag: 'span',
17888                 cls: 'roo-combobox-list-group-item-value'
17889             },
17890             {
17891                 tag: 'div',
17892                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17893                 cn: [
17894                     {
17895                         tag: 'input',
17896                         type: 'radio'
17897                     },
17898                     {
17899                         tag: 'label'
17900                     }
17901                 ]
17902             }
17903         ]
17904     },
17905     
17906     listItemCheckbox : {
17907         tag: 'li',
17908         cls: 'list-group-item',
17909         cn: [
17910             {
17911                 tag: 'span',
17912                 cls: 'roo-combobox-list-group-item-value'
17913             },
17914             {
17915                 tag: 'div',
17916                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17917                 cn: [
17918                     {
17919                         tag: 'input',
17920                         type: 'checkbox'
17921                     },
17922                     {
17923                         tag: 'label'
17924                     }
17925                 ]
17926             }
17927         ]
17928     },
17929     
17930     emptyResult : {
17931         tag: 'div',
17932         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17933     },
17934     
17935     footer : {
17936         tag: 'div',
17937         cls: 'modal-footer',
17938         cn: [
17939             {
17940                 tag: 'div',
17941                 cls: 'row',
17942                 cn: [
17943                     {
17944                         tag: 'div',
17945                         cls: 'col-xs-6 text-left',
17946                         cn: {
17947                             tag: 'button',
17948                             cls: 'btn btn-danger roo-touch-view-cancel',
17949                             html: 'Cancel'
17950                         }
17951                     },
17952                     {
17953                         tag: 'div',
17954                         cls: 'col-xs-6 text-right',
17955                         cn: {
17956                             tag: 'button',
17957                             cls: 'btn btn-success roo-touch-view-ok',
17958                             html: 'OK'
17959                         }
17960                     }
17961                 ]
17962             }
17963         ]
17964         
17965     }
17966 });
17967
17968 Roo.apply(Roo.bootstrap.ComboBox,  {
17969     
17970     touchViewTemplate : {
17971         tag: 'div',
17972         cls: 'modal fade roo-combobox-touch-view',
17973         cn: [
17974             {
17975                 tag: 'div',
17976                 cls: 'modal-dialog',
17977                 style : 'position:fixed', // we have to fix position....
17978                 cn: [
17979                     {
17980                         tag: 'div',
17981                         cls: 'modal-content',
17982                         cn: [
17983                             Roo.bootstrap.ComboBox.header,
17984                             Roo.bootstrap.ComboBox.body,
17985                             Roo.bootstrap.ComboBox.footer
17986                         ]
17987                     }
17988                 ]
17989             }
17990         ]
17991     }
17992 });/*
17993  * Based on:
17994  * Ext JS Library 1.1.1
17995  * Copyright(c) 2006-2007, Ext JS, LLC.
17996  *
17997  * Originally Released Under LGPL - original licence link has changed is not relivant.
17998  *
17999  * Fork - LGPL
18000  * <script type="text/javascript">
18001  */
18002
18003 /**
18004  * @class Roo.View
18005  * @extends Roo.util.Observable
18006  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18007  * This class also supports single and multi selection modes. <br>
18008  * Create a data model bound view:
18009  <pre><code>
18010  var store = new Roo.data.Store(...);
18011
18012  var view = new Roo.View({
18013     el : "my-element",
18014     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18015  
18016     singleSelect: true,
18017     selectedClass: "ydataview-selected",
18018     store: store
18019  });
18020
18021  // listen for node click?
18022  view.on("click", function(vw, index, node, e){
18023  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18024  });
18025
18026  // load XML data
18027  dataModel.load("foobar.xml");
18028  </code></pre>
18029  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18030  * <br><br>
18031  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18032  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18033  * 
18034  * Note: old style constructor is still suported (container, template, config)
18035  * 
18036  * @constructor
18037  * Create a new View
18038  * @param {Object} config The config object
18039  * 
18040  */
18041 Roo.View = function(config, depreciated_tpl, depreciated_config){
18042     
18043     this.parent = false;
18044     
18045     if (typeof(depreciated_tpl) == 'undefined') {
18046         // new way.. - universal constructor.
18047         Roo.apply(this, config);
18048         this.el  = Roo.get(this.el);
18049     } else {
18050         // old format..
18051         this.el  = Roo.get(config);
18052         this.tpl = depreciated_tpl;
18053         Roo.apply(this, depreciated_config);
18054     }
18055     this.wrapEl  = this.el.wrap().wrap();
18056     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18057     
18058     
18059     if(typeof(this.tpl) == "string"){
18060         this.tpl = new Roo.Template(this.tpl);
18061     } else {
18062         // support xtype ctors..
18063         this.tpl = new Roo.factory(this.tpl, Roo);
18064     }
18065     
18066     
18067     this.tpl.compile();
18068     
18069     /** @private */
18070     this.addEvents({
18071         /**
18072          * @event beforeclick
18073          * Fires before a click is processed. Returns false to cancel the default action.
18074          * @param {Roo.View} this
18075          * @param {Number} index The index of the target node
18076          * @param {HTMLElement} node The target node
18077          * @param {Roo.EventObject} e The raw event object
18078          */
18079             "beforeclick" : true,
18080         /**
18081          * @event click
18082          * Fires when a template node is clicked.
18083          * @param {Roo.View} this
18084          * @param {Number} index The index of the target node
18085          * @param {HTMLElement} node The target node
18086          * @param {Roo.EventObject} e The raw event object
18087          */
18088             "click" : true,
18089         /**
18090          * @event dblclick
18091          * Fires when a template node is double clicked.
18092          * @param {Roo.View} this
18093          * @param {Number} index The index of the target node
18094          * @param {HTMLElement} node The target node
18095          * @param {Roo.EventObject} e The raw event object
18096          */
18097             "dblclick" : true,
18098         /**
18099          * @event contextmenu
18100          * Fires when a template node is right clicked.
18101          * @param {Roo.View} this
18102          * @param {Number} index The index of the target node
18103          * @param {HTMLElement} node The target node
18104          * @param {Roo.EventObject} e The raw event object
18105          */
18106             "contextmenu" : true,
18107         /**
18108          * @event selectionchange
18109          * Fires when the selected nodes change.
18110          * @param {Roo.View} this
18111          * @param {Array} selections Array of the selected nodes
18112          */
18113             "selectionchange" : true,
18114     
18115         /**
18116          * @event beforeselect
18117          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18118          * @param {Roo.View} this
18119          * @param {HTMLElement} node The node to be selected
18120          * @param {Array} selections Array of currently selected nodes
18121          */
18122             "beforeselect" : true,
18123         /**
18124          * @event preparedata
18125          * Fires on every row to render, to allow you to change the data.
18126          * @param {Roo.View} this
18127          * @param {Object} data to be rendered (change this)
18128          */
18129           "preparedata" : true
18130           
18131           
18132         });
18133
18134
18135
18136     this.el.on({
18137         "click": this.onClick,
18138         "dblclick": this.onDblClick,
18139         "contextmenu": this.onContextMenu,
18140         scope:this
18141     });
18142
18143     this.selections = [];
18144     this.nodes = [];
18145     this.cmp = new Roo.CompositeElementLite([]);
18146     if(this.store){
18147         this.store = Roo.factory(this.store, Roo.data);
18148         this.setStore(this.store, true);
18149     }
18150     
18151     if ( this.footer && this.footer.xtype) {
18152            
18153          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18154         
18155         this.footer.dataSource = this.store;
18156         this.footer.container = fctr;
18157         this.footer = Roo.factory(this.footer, Roo);
18158         fctr.insertFirst(this.el);
18159         
18160         // this is a bit insane - as the paging toolbar seems to detach the el..
18161 //        dom.parentNode.parentNode.parentNode
18162          // they get detached?
18163     }
18164     
18165     
18166     Roo.View.superclass.constructor.call(this);
18167     
18168     
18169 };
18170
18171 Roo.extend(Roo.View, Roo.util.Observable, {
18172     
18173      /**
18174      * @cfg {Roo.data.Store} store Data store to load data from.
18175      */
18176     store : false,
18177     
18178     /**
18179      * @cfg {String|Roo.Element} el The container element.
18180      */
18181     el : '',
18182     
18183     /**
18184      * @cfg {String|Roo.Template} tpl The template used by this View 
18185      */
18186     tpl : false,
18187     /**
18188      * @cfg {String} dataName the named area of the template to use as the data area
18189      *                          Works with domtemplates roo-name="name"
18190      */
18191     dataName: false,
18192     /**
18193      * @cfg {String} selectedClass The css class to add to selected nodes
18194      */
18195     selectedClass : "x-view-selected",
18196      /**
18197      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18198      */
18199     emptyText : "",
18200     
18201     /**
18202      * @cfg {String} text to display on mask (default Loading)
18203      */
18204     mask : false,
18205     /**
18206      * @cfg {Boolean} multiSelect Allow multiple selection
18207      */
18208     multiSelect : false,
18209     /**
18210      * @cfg {Boolean} singleSelect Allow single selection
18211      */
18212     singleSelect:  false,
18213     
18214     /**
18215      * @cfg {Boolean} toggleSelect - selecting 
18216      */
18217     toggleSelect : false,
18218     
18219     /**
18220      * @cfg {Boolean} tickable - selecting 
18221      */
18222     tickable : false,
18223     
18224     /**
18225      * Returns the element this view is bound to.
18226      * @return {Roo.Element}
18227      */
18228     getEl : function(){
18229         return this.wrapEl;
18230     },
18231     
18232     
18233
18234     /**
18235      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18236      */
18237     refresh : function(){
18238         //Roo.log('refresh');
18239         var t = this.tpl;
18240         
18241         // if we are using something like 'domtemplate', then
18242         // the what gets used is:
18243         // t.applySubtemplate(NAME, data, wrapping data..)
18244         // the outer template then get' applied with
18245         //     the store 'extra data'
18246         // and the body get's added to the
18247         //      roo-name="data" node?
18248         //      <span class='roo-tpl-{name}'></span> ?????
18249         
18250         
18251         
18252         this.clearSelections();
18253         this.el.update("");
18254         var html = [];
18255         var records = this.store.getRange();
18256         if(records.length < 1) {
18257             
18258             // is this valid??  = should it render a template??
18259             
18260             this.el.update(this.emptyText);
18261             return;
18262         }
18263         var el = this.el;
18264         if (this.dataName) {
18265             this.el.update(t.apply(this.store.meta)); //????
18266             el = this.el.child('.roo-tpl-' + this.dataName);
18267         }
18268         
18269         for(var i = 0, len = records.length; i < len; i++){
18270             var data = this.prepareData(records[i].data, i, records[i]);
18271             this.fireEvent("preparedata", this, data, i, records[i]);
18272             
18273             var d = Roo.apply({}, data);
18274             
18275             if(this.tickable){
18276                 Roo.apply(d, {'roo-id' : Roo.id()});
18277                 
18278                 var _this = this;
18279             
18280                 Roo.each(this.parent.item, function(item){
18281                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18282                         return;
18283                     }
18284                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18285                 });
18286             }
18287             
18288             html[html.length] = Roo.util.Format.trim(
18289                 this.dataName ?
18290                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18291                     t.apply(d)
18292             );
18293         }
18294         
18295         
18296         
18297         el.update(html.join(""));
18298         this.nodes = el.dom.childNodes;
18299         this.updateIndexes(0);
18300     },
18301     
18302
18303     /**
18304      * Function to override to reformat the data that is sent to
18305      * the template for each node.
18306      * DEPRICATED - use the preparedata event handler.
18307      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18308      * a JSON object for an UpdateManager bound view).
18309      */
18310     prepareData : function(data, index, record)
18311     {
18312         this.fireEvent("preparedata", this, data, index, record);
18313         return data;
18314     },
18315
18316     onUpdate : function(ds, record){
18317         // Roo.log('on update');   
18318         this.clearSelections();
18319         var index = this.store.indexOf(record);
18320         var n = this.nodes[index];
18321         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18322         n.parentNode.removeChild(n);
18323         this.updateIndexes(index, index);
18324     },
18325
18326     
18327     
18328 // --------- FIXME     
18329     onAdd : function(ds, records, index)
18330     {
18331         //Roo.log(['on Add', ds, records, index] );        
18332         this.clearSelections();
18333         if(this.nodes.length == 0){
18334             this.refresh();
18335             return;
18336         }
18337         var n = this.nodes[index];
18338         for(var i = 0, len = records.length; i < len; i++){
18339             var d = this.prepareData(records[i].data, i, records[i]);
18340             if(n){
18341                 this.tpl.insertBefore(n, d);
18342             }else{
18343                 
18344                 this.tpl.append(this.el, d);
18345             }
18346         }
18347         this.updateIndexes(index);
18348     },
18349
18350     onRemove : function(ds, record, index){
18351        // Roo.log('onRemove');
18352         this.clearSelections();
18353         var el = this.dataName  ?
18354             this.el.child('.roo-tpl-' + this.dataName) :
18355             this.el; 
18356         
18357         el.dom.removeChild(this.nodes[index]);
18358         this.updateIndexes(index);
18359     },
18360
18361     /**
18362      * Refresh an individual node.
18363      * @param {Number} index
18364      */
18365     refreshNode : function(index){
18366         this.onUpdate(this.store, this.store.getAt(index));
18367     },
18368
18369     updateIndexes : function(startIndex, endIndex){
18370         var ns = this.nodes;
18371         startIndex = startIndex || 0;
18372         endIndex = endIndex || ns.length - 1;
18373         for(var i = startIndex; i <= endIndex; i++){
18374             ns[i].nodeIndex = i;
18375         }
18376     },
18377
18378     /**
18379      * Changes the data store this view uses and refresh the view.
18380      * @param {Store} store
18381      */
18382     setStore : function(store, initial){
18383         if(!initial && this.store){
18384             this.store.un("datachanged", this.refresh);
18385             this.store.un("add", this.onAdd);
18386             this.store.un("remove", this.onRemove);
18387             this.store.un("update", this.onUpdate);
18388             this.store.un("clear", this.refresh);
18389             this.store.un("beforeload", this.onBeforeLoad);
18390             this.store.un("load", this.onLoad);
18391             this.store.un("loadexception", this.onLoad);
18392         }
18393         if(store){
18394           
18395             store.on("datachanged", this.refresh, this);
18396             store.on("add", this.onAdd, this);
18397             store.on("remove", this.onRemove, this);
18398             store.on("update", this.onUpdate, this);
18399             store.on("clear", this.refresh, this);
18400             store.on("beforeload", this.onBeforeLoad, this);
18401             store.on("load", this.onLoad, this);
18402             store.on("loadexception", this.onLoad, this);
18403         }
18404         
18405         if(store){
18406             this.refresh();
18407         }
18408     },
18409     /**
18410      * onbeforeLoad - masks the loading area.
18411      *
18412      */
18413     onBeforeLoad : function(store,opts)
18414     {
18415          //Roo.log('onBeforeLoad');   
18416         if (!opts.add) {
18417             this.el.update("");
18418         }
18419         this.el.mask(this.mask ? this.mask : "Loading" ); 
18420     },
18421     onLoad : function ()
18422     {
18423         this.el.unmask();
18424     },
18425     
18426
18427     /**
18428      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18429      * @param {HTMLElement} node
18430      * @return {HTMLElement} The template node
18431      */
18432     findItemFromChild : function(node){
18433         var el = this.dataName  ?
18434             this.el.child('.roo-tpl-' + this.dataName,true) :
18435             this.el.dom; 
18436         
18437         if(!node || node.parentNode == el){
18438                     return node;
18439             }
18440             var p = node.parentNode;
18441             while(p && p != el){
18442             if(p.parentNode == el){
18443                 return p;
18444             }
18445             p = p.parentNode;
18446         }
18447             return null;
18448     },
18449
18450     /** @ignore */
18451     onClick : function(e){
18452         var item = this.findItemFromChild(e.getTarget());
18453         if(item){
18454             var index = this.indexOf(item);
18455             if(this.onItemClick(item, index, e) !== false){
18456                 this.fireEvent("click", this, index, item, e);
18457             }
18458         }else{
18459             this.clearSelections();
18460         }
18461     },
18462
18463     /** @ignore */
18464     onContextMenu : function(e){
18465         var item = this.findItemFromChild(e.getTarget());
18466         if(item){
18467             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18468         }
18469     },
18470
18471     /** @ignore */
18472     onDblClick : function(e){
18473         var item = this.findItemFromChild(e.getTarget());
18474         if(item){
18475             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18476         }
18477     },
18478
18479     onItemClick : function(item, index, e)
18480     {
18481         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18482             return false;
18483         }
18484         if (this.toggleSelect) {
18485             var m = this.isSelected(item) ? 'unselect' : 'select';
18486             //Roo.log(m);
18487             var _t = this;
18488             _t[m](item, true, false);
18489             return true;
18490         }
18491         if(this.multiSelect || this.singleSelect){
18492             if(this.multiSelect && e.shiftKey && this.lastSelection){
18493                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18494             }else{
18495                 this.select(item, this.multiSelect && e.ctrlKey);
18496                 this.lastSelection = item;
18497             }
18498             
18499             if(!this.tickable){
18500                 e.preventDefault();
18501             }
18502             
18503         }
18504         return true;
18505     },
18506
18507     /**
18508      * Get the number of selected nodes.
18509      * @return {Number}
18510      */
18511     getSelectionCount : function(){
18512         return this.selections.length;
18513     },
18514
18515     /**
18516      * Get the currently selected nodes.
18517      * @return {Array} An array of HTMLElements
18518      */
18519     getSelectedNodes : function(){
18520         return this.selections;
18521     },
18522
18523     /**
18524      * Get the indexes of the selected nodes.
18525      * @return {Array}
18526      */
18527     getSelectedIndexes : function(){
18528         var indexes = [], s = this.selections;
18529         for(var i = 0, len = s.length; i < len; i++){
18530             indexes.push(s[i].nodeIndex);
18531         }
18532         return indexes;
18533     },
18534
18535     /**
18536      * Clear all selections
18537      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18538      */
18539     clearSelections : function(suppressEvent){
18540         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18541             this.cmp.elements = this.selections;
18542             this.cmp.removeClass(this.selectedClass);
18543             this.selections = [];
18544             if(!suppressEvent){
18545                 this.fireEvent("selectionchange", this, this.selections);
18546             }
18547         }
18548     },
18549
18550     /**
18551      * Returns true if the passed node is selected
18552      * @param {HTMLElement/Number} node The node or node index
18553      * @return {Boolean}
18554      */
18555     isSelected : function(node){
18556         var s = this.selections;
18557         if(s.length < 1){
18558             return false;
18559         }
18560         node = this.getNode(node);
18561         return s.indexOf(node) !== -1;
18562     },
18563
18564     /**
18565      * Selects nodes.
18566      * @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
18567      * @param {Boolean} keepExisting (optional) true to keep existing selections
18568      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18569      */
18570     select : function(nodeInfo, keepExisting, suppressEvent){
18571         if(nodeInfo instanceof Array){
18572             if(!keepExisting){
18573                 this.clearSelections(true);
18574             }
18575             for(var i = 0, len = nodeInfo.length; i < len; i++){
18576                 this.select(nodeInfo[i], true, true);
18577             }
18578             return;
18579         } 
18580         var node = this.getNode(nodeInfo);
18581         if(!node || this.isSelected(node)){
18582             return; // already selected.
18583         }
18584         if(!keepExisting){
18585             this.clearSelections(true);
18586         }
18587         
18588         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18589             Roo.fly(node).addClass(this.selectedClass);
18590             this.selections.push(node);
18591             if(!suppressEvent){
18592                 this.fireEvent("selectionchange", this, this.selections);
18593             }
18594         }
18595         
18596         
18597     },
18598       /**
18599      * Unselects nodes.
18600      * @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
18601      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18602      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18603      */
18604     unselect : function(nodeInfo, keepExisting, suppressEvent)
18605     {
18606         if(nodeInfo instanceof Array){
18607             Roo.each(this.selections, function(s) {
18608                 this.unselect(s, nodeInfo);
18609             }, this);
18610             return;
18611         }
18612         var node = this.getNode(nodeInfo);
18613         if(!node || !this.isSelected(node)){
18614             //Roo.log("not selected");
18615             return; // not selected.
18616         }
18617         // fireevent???
18618         var ns = [];
18619         Roo.each(this.selections, function(s) {
18620             if (s == node ) {
18621                 Roo.fly(node).removeClass(this.selectedClass);
18622
18623                 return;
18624             }
18625             ns.push(s);
18626         },this);
18627         
18628         this.selections= ns;
18629         this.fireEvent("selectionchange", this, this.selections);
18630     },
18631
18632     /**
18633      * Gets a template node.
18634      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18635      * @return {HTMLElement} The node or null if it wasn't found
18636      */
18637     getNode : function(nodeInfo){
18638         if(typeof nodeInfo == "string"){
18639             return document.getElementById(nodeInfo);
18640         }else if(typeof nodeInfo == "number"){
18641             return this.nodes[nodeInfo];
18642         }
18643         return nodeInfo;
18644     },
18645
18646     /**
18647      * Gets a range template nodes.
18648      * @param {Number} startIndex
18649      * @param {Number} endIndex
18650      * @return {Array} An array of nodes
18651      */
18652     getNodes : function(start, end){
18653         var ns = this.nodes;
18654         start = start || 0;
18655         end = typeof end == "undefined" ? ns.length - 1 : end;
18656         var nodes = [];
18657         if(start <= end){
18658             for(var i = start; i <= end; i++){
18659                 nodes.push(ns[i]);
18660             }
18661         } else{
18662             for(var i = start; i >= end; i--){
18663                 nodes.push(ns[i]);
18664             }
18665         }
18666         return nodes;
18667     },
18668
18669     /**
18670      * Finds the index of the passed node
18671      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18672      * @return {Number} The index of the node or -1
18673      */
18674     indexOf : function(node){
18675         node = this.getNode(node);
18676         if(typeof node.nodeIndex == "number"){
18677             return node.nodeIndex;
18678         }
18679         var ns = this.nodes;
18680         for(var i = 0, len = ns.length; i < len; i++){
18681             if(ns[i] == node){
18682                 return i;
18683             }
18684         }
18685         return -1;
18686     }
18687 });
18688 /*
18689  * - LGPL
18690  *
18691  * based on jquery fullcalendar
18692  * 
18693  */
18694
18695 Roo.bootstrap = Roo.bootstrap || {};
18696 /**
18697  * @class Roo.bootstrap.Calendar
18698  * @extends Roo.bootstrap.Component
18699  * Bootstrap Calendar class
18700  * @cfg {Boolean} loadMask (true|false) default false
18701  * @cfg {Object} header generate the user specific header of the calendar, default false
18702
18703  * @constructor
18704  * Create a new Container
18705  * @param {Object} config The config object
18706  */
18707
18708
18709
18710 Roo.bootstrap.Calendar = function(config){
18711     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18712      this.addEvents({
18713         /**
18714              * @event select
18715              * Fires when a date is selected
18716              * @param {DatePicker} this
18717              * @param {Date} date The selected date
18718              */
18719         'select': true,
18720         /**
18721              * @event monthchange
18722              * Fires when the displayed month changes 
18723              * @param {DatePicker} this
18724              * @param {Date} date The selected month
18725              */
18726         'monthchange': true,
18727         /**
18728              * @event evententer
18729              * Fires when mouse over an event
18730              * @param {Calendar} this
18731              * @param {event} Event
18732              */
18733         'evententer': true,
18734         /**
18735              * @event eventleave
18736              * Fires when the mouse leaves an
18737              * @param {Calendar} this
18738              * @param {event}
18739              */
18740         'eventleave': true,
18741         /**
18742              * @event eventclick
18743              * Fires when the mouse click an
18744              * @param {Calendar} this
18745              * @param {event}
18746              */
18747         'eventclick': true
18748         
18749     });
18750
18751 };
18752
18753 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
18754     
18755      /**
18756      * @cfg {Number} startDay
18757      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18758      */
18759     startDay : 0,
18760     
18761     loadMask : false,
18762     
18763     header : false,
18764       
18765     getAutoCreate : function(){
18766         
18767         
18768         var fc_button = function(name, corner, style, content ) {
18769             return Roo.apply({},{
18770                 tag : 'span',
18771                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
18772                          (corner.length ?
18773                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18774                             ''
18775                         ),
18776                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18777                 unselectable: 'on'
18778             });
18779         };
18780         
18781         var header = {};
18782         
18783         if(!this.header){
18784             header = {
18785                 tag : 'table',
18786                 cls : 'fc-header',
18787                 style : 'width:100%',
18788                 cn : [
18789                     {
18790                         tag: 'tr',
18791                         cn : [
18792                             {
18793                                 tag : 'td',
18794                                 cls : 'fc-header-left',
18795                                 cn : [
18796                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
18797                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
18798                                     { tag: 'span', cls: 'fc-header-space' },
18799                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
18800
18801
18802                                 ]
18803                             },
18804
18805                             {
18806                                 tag : 'td',
18807                                 cls : 'fc-header-center',
18808                                 cn : [
18809                                     {
18810                                         tag: 'span',
18811                                         cls: 'fc-header-title',
18812                                         cn : {
18813                                             tag: 'H2',
18814                                             html : 'month / year'
18815                                         }
18816                                     }
18817
18818                                 ]
18819                             },
18820                             {
18821                                 tag : 'td',
18822                                 cls : 'fc-header-right',
18823                                 cn : [
18824                               /*      fc_button('month', 'left', '', 'month' ),
18825                                     fc_button('week', '', '', 'week' ),
18826                                     fc_button('day', 'right', '', 'day' )
18827                                 */    
18828
18829                                 ]
18830                             }
18831
18832                         ]
18833                     }
18834                 ]
18835             };
18836         }
18837         
18838         header = this.header;
18839         
18840        
18841         var cal_heads = function() {
18842             var ret = [];
18843             // fixme - handle this.
18844             
18845             for (var i =0; i < Date.dayNames.length; i++) {
18846                 var d = Date.dayNames[i];
18847                 ret.push({
18848                     tag: 'th',
18849                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18850                     html : d.substring(0,3)
18851                 });
18852                 
18853             }
18854             ret[0].cls += ' fc-first';
18855             ret[6].cls += ' fc-last';
18856             return ret;
18857         };
18858         var cal_cell = function(n) {
18859             return  {
18860                 tag: 'td',
18861                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18862                 cn : [
18863                     {
18864                         cn : [
18865                             {
18866                                 cls: 'fc-day-number',
18867                                 html: 'D'
18868                             },
18869                             {
18870                                 cls: 'fc-day-content',
18871                              
18872                                 cn : [
18873                                      {
18874                                         style: 'position: relative;' // height: 17px;
18875                                     }
18876                                 ]
18877                             }
18878                             
18879                             
18880                         ]
18881                     }
18882                 ]
18883                 
18884             }
18885         };
18886         var cal_rows = function() {
18887             
18888             var ret = [];
18889             for (var r = 0; r < 6; r++) {
18890                 var row= {
18891                     tag : 'tr',
18892                     cls : 'fc-week',
18893                     cn : []
18894                 };
18895                 
18896                 for (var i =0; i < Date.dayNames.length; i++) {
18897                     var d = Date.dayNames[i];
18898                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18899
18900                 }
18901                 row.cn[0].cls+=' fc-first';
18902                 row.cn[0].cn[0].style = 'min-height:90px';
18903                 row.cn[6].cls+=' fc-last';
18904                 ret.push(row);
18905                 
18906             }
18907             ret[0].cls += ' fc-first';
18908             ret[4].cls += ' fc-prev-last';
18909             ret[5].cls += ' fc-last';
18910             return ret;
18911             
18912         };
18913         
18914         var cal_table = {
18915             tag: 'table',
18916             cls: 'fc-border-separate',
18917             style : 'width:100%',
18918             cellspacing  : 0,
18919             cn : [
18920                 { 
18921                     tag: 'thead',
18922                     cn : [
18923                         { 
18924                             tag: 'tr',
18925                             cls : 'fc-first fc-last',
18926                             cn : cal_heads()
18927                         }
18928                     ]
18929                 },
18930                 { 
18931                     tag: 'tbody',
18932                     cn : cal_rows()
18933                 }
18934                   
18935             ]
18936         };
18937          
18938          var cfg = {
18939             cls : 'fc fc-ltr',
18940             cn : [
18941                 header,
18942                 {
18943                     cls : 'fc-content',
18944                     style : "position: relative;",
18945                     cn : [
18946                         {
18947                             cls : 'fc-view fc-view-month fc-grid',
18948                             style : 'position: relative',
18949                             unselectable : 'on',
18950                             cn : [
18951                                 {
18952                                     cls : 'fc-event-container',
18953                                     style : 'position:absolute;z-index:8;top:0;left:0;'
18954                                 },
18955                                 cal_table
18956                             ]
18957                         }
18958                     ]
18959     
18960                 }
18961            ] 
18962             
18963         };
18964         
18965          
18966         
18967         return cfg;
18968     },
18969     
18970     
18971     initEvents : function()
18972     {
18973         if(!this.store){
18974             throw "can not find store for calendar";
18975         }
18976         
18977         var mark = {
18978             tag: "div",
18979             cls:"x-dlg-mask",
18980             style: "text-align:center",
18981             cn: [
18982                 {
18983                     tag: "div",
18984                     style: "background-color:white;width:50%;margin:250 auto",
18985                     cn: [
18986                         {
18987                             tag: "img",
18988                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
18989                         },
18990                         {
18991                             tag: "span",
18992                             html: "Loading"
18993                         }
18994                         
18995                     ]
18996                 }
18997             ]
18998         };
18999         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19000         
19001         var size = this.el.select('.fc-content', true).first().getSize();
19002         this.maskEl.setSize(size.width, size.height);
19003         this.maskEl.enableDisplayMode("block");
19004         if(!this.loadMask){
19005             this.maskEl.hide();
19006         }
19007         
19008         this.store = Roo.factory(this.store, Roo.data);
19009         this.store.on('load', this.onLoad, this);
19010         this.store.on('beforeload', this.onBeforeLoad, this);
19011         
19012         this.resize();
19013         
19014         this.cells = this.el.select('.fc-day',true);
19015         //Roo.log(this.cells);
19016         this.textNodes = this.el.query('.fc-day-number');
19017         this.cells.addClassOnOver('fc-state-hover');
19018         
19019         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19020         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19021         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19022         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19023         
19024         this.on('monthchange', this.onMonthChange, this);
19025         
19026         this.update(new Date().clearTime());
19027     },
19028     
19029     resize : function() {
19030         var sz  = this.el.getSize();
19031         
19032         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19033         this.el.select('.fc-day-content div',true).setHeight(34);
19034     },
19035     
19036     
19037     // private
19038     showPrevMonth : function(e){
19039         this.update(this.activeDate.add("mo", -1));
19040     },
19041     showToday : function(e){
19042         this.update(new Date().clearTime());
19043     },
19044     // private
19045     showNextMonth : function(e){
19046         this.update(this.activeDate.add("mo", 1));
19047     },
19048
19049     // private
19050     showPrevYear : function(){
19051         this.update(this.activeDate.add("y", -1));
19052     },
19053
19054     // private
19055     showNextYear : function(){
19056         this.update(this.activeDate.add("y", 1));
19057     },
19058
19059     
19060    // private
19061     update : function(date)
19062     {
19063         var vd = this.activeDate;
19064         this.activeDate = date;
19065 //        if(vd && this.el){
19066 //            var t = date.getTime();
19067 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19068 //                Roo.log('using add remove');
19069 //                
19070 //                this.fireEvent('monthchange', this, date);
19071 //                
19072 //                this.cells.removeClass("fc-state-highlight");
19073 //                this.cells.each(function(c){
19074 //                   if(c.dateValue == t){
19075 //                       c.addClass("fc-state-highlight");
19076 //                       setTimeout(function(){
19077 //                            try{c.dom.firstChild.focus();}catch(e){}
19078 //                       }, 50);
19079 //                       return false;
19080 //                   }
19081 //                   return true;
19082 //                });
19083 //                return;
19084 //            }
19085 //        }
19086         
19087         var days = date.getDaysInMonth();
19088         
19089         var firstOfMonth = date.getFirstDateOfMonth();
19090         var startingPos = firstOfMonth.getDay()-this.startDay;
19091         
19092         if(startingPos < this.startDay){
19093             startingPos += 7;
19094         }
19095         
19096         var pm = date.add(Date.MONTH, -1);
19097         var prevStart = pm.getDaysInMonth()-startingPos;
19098 //        
19099         this.cells = this.el.select('.fc-day',true);
19100         this.textNodes = this.el.query('.fc-day-number');
19101         this.cells.addClassOnOver('fc-state-hover');
19102         
19103         var cells = this.cells.elements;
19104         var textEls = this.textNodes;
19105         
19106         Roo.each(cells, function(cell){
19107             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19108         });
19109         
19110         days += startingPos;
19111
19112         // convert everything to numbers so it's fast
19113         var day = 86400000;
19114         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19115         //Roo.log(d);
19116         //Roo.log(pm);
19117         //Roo.log(prevStart);
19118         
19119         var today = new Date().clearTime().getTime();
19120         var sel = date.clearTime().getTime();
19121         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19122         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19123         var ddMatch = this.disabledDatesRE;
19124         var ddText = this.disabledDatesText;
19125         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19126         var ddaysText = this.disabledDaysText;
19127         var format = this.format;
19128         
19129         var setCellClass = function(cal, cell){
19130             cell.row = 0;
19131             cell.events = [];
19132             cell.more = [];
19133             //Roo.log('set Cell Class');
19134             cell.title = "";
19135             var t = d.getTime();
19136             
19137             //Roo.log(d);
19138             
19139             cell.dateValue = t;
19140             if(t == today){
19141                 cell.className += " fc-today";
19142                 cell.className += " fc-state-highlight";
19143                 cell.title = cal.todayText;
19144             }
19145             if(t == sel){
19146                 // disable highlight in other month..
19147                 //cell.className += " fc-state-highlight";
19148                 
19149             }
19150             // disabling
19151             if(t < min) {
19152                 cell.className = " fc-state-disabled";
19153                 cell.title = cal.minText;
19154                 return;
19155             }
19156             if(t > max) {
19157                 cell.className = " fc-state-disabled";
19158                 cell.title = cal.maxText;
19159                 return;
19160             }
19161             if(ddays){
19162                 if(ddays.indexOf(d.getDay()) != -1){
19163                     cell.title = ddaysText;
19164                     cell.className = " fc-state-disabled";
19165                 }
19166             }
19167             if(ddMatch && format){
19168                 var fvalue = d.dateFormat(format);
19169                 if(ddMatch.test(fvalue)){
19170                     cell.title = ddText.replace("%0", fvalue);
19171                     cell.className = " fc-state-disabled";
19172                 }
19173             }
19174             
19175             if (!cell.initialClassName) {
19176                 cell.initialClassName = cell.dom.className;
19177             }
19178             
19179             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19180         };
19181
19182         var i = 0;
19183         
19184         for(; i < startingPos; i++) {
19185             textEls[i].innerHTML = (++prevStart);
19186             d.setDate(d.getDate()+1);
19187             
19188             cells[i].className = "fc-past fc-other-month";
19189             setCellClass(this, cells[i]);
19190         }
19191         
19192         var intDay = 0;
19193         
19194         for(; i < days; i++){
19195             intDay = i - startingPos + 1;
19196             textEls[i].innerHTML = (intDay);
19197             d.setDate(d.getDate()+1);
19198             
19199             cells[i].className = ''; // "x-date-active";
19200             setCellClass(this, cells[i]);
19201         }
19202         var extraDays = 0;
19203         
19204         for(; i < 42; i++) {
19205             textEls[i].innerHTML = (++extraDays);
19206             d.setDate(d.getDate()+1);
19207             
19208             cells[i].className = "fc-future fc-other-month";
19209             setCellClass(this, cells[i]);
19210         }
19211         
19212         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19213         
19214         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19215         
19216         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19217         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19218         
19219         if(totalRows != 6){
19220             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19221             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19222         }
19223         
19224         this.fireEvent('monthchange', this, date);
19225         
19226         
19227         /*
19228         if(!this.internalRender){
19229             var main = this.el.dom.firstChild;
19230             var w = main.offsetWidth;
19231             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19232             Roo.fly(main).setWidth(w);
19233             this.internalRender = true;
19234             // opera does not respect the auto grow header center column
19235             // then, after it gets a width opera refuses to recalculate
19236             // without a second pass
19237             if(Roo.isOpera && !this.secondPass){
19238                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19239                 this.secondPass = true;
19240                 this.update.defer(10, this, [date]);
19241             }
19242         }
19243         */
19244         
19245     },
19246     
19247     findCell : function(dt) {
19248         dt = dt.clearTime().getTime();
19249         var ret = false;
19250         this.cells.each(function(c){
19251             //Roo.log("check " +c.dateValue + '?=' + dt);
19252             if(c.dateValue == dt){
19253                 ret = c;
19254                 return false;
19255             }
19256             return true;
19257         });
19258         
19259         return ret;
19260     },
19261     
19262     findCells : function(ev) {
19263         var s = ev.start.clone().clearTime().getTime();
19264        // Roo.log(s);
19265         var e= ev.end.clone().clearTime().getTime();
19266        // Roo.log(e);
19267         var ret = [];
19268         this.cells.each(function(c){
19269              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19270             
19271             if(c.dateValue > e){
19272                 return ;
19273             }
19274             if(c.dateValue < s){
19275                 return ;
19276             }
19277             ret.push(c);
19278         });
19279         
19280         return ret;    
19281     },
19282     
19283 //    findBestRow: function(cells)
19284 //    {
19285 //        var ret = 0;
19286 //        
19287 //        for (var i =0 ; i < cells.length;i++) {
19288 //            ret  = Math.max(cells[i].rows || 0,ret);
19289 //        }
19290 //        return ret;
19291 //        
19292 //    },
19293     
19294     
19295     addItem : function(ev)
19296     {
19297         // look for vertical location slot in
19298         var cells = this.findCells(ev);
19299         
19300 //        ev.row = this.findBestRow(cells);
19301         
19302         // work out the location.
19303         
19304         var crow = false;
19305         var rows = [];
19306         for(var i =0; i < cells.length; i++) {
19307             
19308             cells[i].row = cells[0].row;
19309             
19310             if(i == 0){
19311                 cells[i].row = cells[i].row + 1;
19312             }
19313             
19314             if (!crow) {
19315                 crow = {
19316                     start : cells[i],
19317                     end :  cells[i]
19318                 };
19319                 continue;
19320             }
19321             if (crow.start.getY() == cells[i].getY()) {
19322                 // on same row.
19323                 crow.end = cells[i];
19324                 continue;
19325             }
19326             // different row.
19327             rows.push(crow);
19328             crow = {
19329                 start: cells[i],
19330                 end : cells[i]
19331             };
19332             
19333         }
19334         
19335         rows.push(crow);
19336         ev.els = [];
19337         ev.rows = rows;
19338         ev.cells = cells;
19339         
19340         cells[0].events.push(ev);
19341         
19342         this.calevents.push(ev);
19343     },
19344     
19345     clearEvents: function() {
19346         
19347         if(!this.calevents){
19348             return;
19349         }
19350         
19351         Roo.each(this.cells.elements, function(c){
19352             c.row = 0;
19353             c.events = [];
19354             c.more = [];
19355         });
19356         
19357         Roo.each(this.calevents, function(e) {
19358             Roo.each(e.els, function(el) {
19359                 el.un('mouseenter' ,this.onEventEnter, this);
19360                 el.un('mouseleave' ,this.onEventLeave, this);
19361                 el.remove();
19362             },this);
19363         },this);
19364         
19365         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19366             e.remove();
19367         });
19368         
19369     },
19370     
19371     renderEvents: function()
19372     {   
19373         var _this = this;
19374         
19375         this.cells.each(function(c) {
19376             
19377             if(c.row < 5){
19378                 return;
19379             }
19380             
19381             var ev = c.events;
19382             
19383             var r = 4;
19384             if(c.row != c.events.length){
19385                 r = 4 - (4 - (c.row - c.events.length));
19386             }
19387             
19388             c.events = ev.slice(0, r);
19389             c.more = ev.slice(r);
19390             
19391             if(c.more.length && c.more.length == 1){
19392                 c.events.push(c.more.pop());
19393             }
19394             
19395             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19396             
19397         });
19398             
19399         this.cells.each(function(c) {
19400             
19401             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19402             
19403             
19404             for (var e = 0; e < c.events.length; e++){
19405                 var ev = c.events[e];
19406                 var rows = ev.rows;
19407                 
19408                 for(var i = 0; i < rows.length; i++) {
19409                 
19410                     // how many rows should it span..
19411
19412                     var  cfg = {
19413                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19414                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19415
19416                         unselectable : "on",
19417                         cn : [
19418                             {
19419                                 cls: 'fc-event-inner',
19420                                 cn : [
19421     //                                {
19422     //                                  tag:'span',
19423     //                                  cls: 'fc-event-time',
19424     //                                  html : cells.length > 1 ? '' : ev.time
19425     //                                },
19426                                     {
19427                                       tag:'span',
19428                                       cls: 'fc-event-title',
19429                                       html : String.format('{0}', ev.title)
19430                                     }
19431
19432
19433                                 ]
19434                             },
19435                             {
19436                                 cls: 'ui-resizable-handle ui-resizable-e',
19437                                 html : '&nbsp;&nbsp;&nbsp'
19438                             }
19439
19440                         ]
19441                     };
19442
19443                     if (i == 0) {
19444                         cfg.cls += ' fc-event-start';
19445                     }
19446                     if ((i+1) == rows.length) {
19447                         cfg.cls += ' fc-event-end';
19448                     }
19449
19450                     var ctr = _this.el.select('.fc-event-container',true).first();
19451                     var cg = ctr.createChild(cfg);
19452
19453                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19454                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19455
19456                     var r = (c.more.length) ? 1 : 0;
19457                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19458                     cg.setWidth(ebox.right - sbox.x -2);
19459
19460                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19461                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19462                     cg.on('click', _this.onEventClick, _this, ev);
19463
19464                     ev.els.push(cg);
19465                     
19466                 }
19467                 
19468             }
19469             
19470             
19471             if(c.more.length){
19472                 var  cfg = {
19473                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19474                     style : 'position: absolute',
19475                     unselectable : "on",
19476                     cn : [
19477                         {
19478                             cls: 'fc-event-inner',
19479                             cn : [
19480                                 {
19481                                   tag:'span',
19482                                   cls: 'fc-event-title',
19483                                   html : 'More'
19484                                 }
19485
19486
19487                             ]
19488                         },
19489                         {
19490                             cls: 'ui-resizable-handle ui-resizable-e',
19491                             html : '&nbsp;&nbsp;&nbsp'
19492                         }
19493
19494                     ]
19495                 };
19496
19497                 var ctr = _this.el.select('.fc-event-container',true).first();
19498                 var cg = ctr.createChild(cfg);
19499
19500                 var sbox = c.select('.fc-day-content',true).first().getBox();
19501                 var ebox = c.select('.fc-day-content',true).first().getBox();
19502                 //Roo.log(cg);
19503                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19504                 cg.setWidth(ebox.right - sbox.x -2);
19505
19506                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19507                 
19508             }
19509             
19510         });
19511         
19512         
19513         
19514     },
19515     
19516     onEventEnter: function (e, el,event,d) {
19517         this.fireEvent('evententer', this, el, event);
19518     },
19519     
19520     onEventLeave: function (e, el,event,d) {
19521         this.fireEvent('eventleave', this, el, event);
19522     },
19523     
19524     onEventClick: function (e, el,event,d) {
19525         this.fireEvent('eventclick', this, el, event);
19526     },
19527     
19528     onMonthChange: function () {
19529         this.store.load();
19530     },
19531     
19532     onMoreEventClick: function(e, el, more)
19533     {
19534         var _this = this;
19535         
19536         this.calpopover.placement = 'right';
19537         this.calpopover.setTitle('More');
19538         
19539         this.calpopover.setContent('');
19540         
19541         var ctr = this.calpopover.el.select('.popover-content', true).first();
19542         
19543         Roo.each(more, function(m){
19544             var cfg = {
19545                 cls : 'fc-event-hori fc-event-draggable',
19546                 html : m.title
19547             };
19548             var cg = ctr.createChild(cfg);
19549             
19550             cg.on('click', _this.onEventClick, _this, m);
19551         });
19552         
19553         this.calpopover.show(el);
19554         
19555         
19556     },
19557     
19558     onLoad: function () 
19559     {   
19560         this.calevents = [];
19561         var cal = this;
19562         
19563         if(this.store.getCount() > 0){
19564             this.store.data.each(function(d){
19565                cal.addItem({
19566                     id : d.data.id,
19567                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19568                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19569                     time : d.data.start_time,
19570                     title : d.data.title,
19571                     description : d.data.description,
19572                     venue : d.data.venue
19573                 });
19574             });
19575         }
19576         
19577         this.renderEvents();
19578         
19579         if(this.calevents.length && this.loadMask){
19580             this.maskEl.hide();
19581         }
19582     },
19583     
19584     onBeforeLoad: function()
19585     {
19586         this.clearEvents();
19587         if(this.loadMask){
19588             this.maskEl.show();
19589         }
19590     }
19591 });
19592
19593  
19594  /*
19595  * - LGPL
19596  *
19597  * element
19598  * 
19599  */
19600
19601 /**
19602  * @class Roo.bootstrap.Popover
19603  * @extends Roo.bootstrap.Component
19604  * Bootstrap Popover class
19605  * @cfg {String} html contents of the popover   (or false to use children..)
19606  * @cfg {String} title of popover (or false to hide)
19607  * @cfg {String} placement how it is placed
19608  * @cfg {String} trigger click || hover (or false to trigger manually)
19609  * @cfg {String} over what (parent or false to trigger manually.)
19610  * @cfg {Number} delay - delay before showing
19611  
19612  * @constructor
19613  * Create a new Popover
19614  * @param {Object} config The config object
19615  */
19616
19617 Roo.bootstrap.Popover = function(config){
19618     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19619     
19620     this.addEvents({
19621         // raw events
19622          /**
19623          * @event show
19624          * After the popover show
19625          * 
19626          * @param {Roo.bootstrap.Popover} this
19627          */
19628         "show" : true,
19629         /**
19630          * @event hide
19631          * After the popover hide
19632          * 
19633          * @param {Roo.bootstrap.Popover} this
19634          */
19635         "hide" : true
19636     });
19637 };
19638
19639 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19640     
19641     title: 'Fill in a title',
19642     html: false,
19643     
19644     placement : 'right',
19645     trigger : 'hover', // hover
19646     
19647     delay : 0,
19648     
19649     over: 'parent',
19650     
19651     can_build_overlaid : false,
19652     
19653     getChildContainer : function()
19654     {
19655         return this.el.select('.popover-content',true).first();
19656     },
19657     
19658     getAutoCreate : function(){
19659          
19660         var cfg = {
19661            cls : 'popover roo-dynamic',
19662            style: 'display:block',
19663            cn : [
19664                 {
19665                     cls : 'arrow'
19666                 },
19667                 {
19668                     cls : 'popover-inner',
19669                     cn : [
19670                         {
19671                             tag: 'h3',
19672                             cls: 'popover-title popover-header',
19673                             html : this.title
19674                         },
19675                         {
19676                             cls : 'popover-content popover-body',
19677                             html : this.html
19678                         }
19679                     ]
19680                     
19681                 }
19682            ]
19683         };
19684         
19685         return cfg;
19686     },
19687     setTitle: function(str)
19688     {
19689         this.title = str;
19690         this.el.select('.popover-title',true).first().dom.innerHTML = str;
19691     },
19692     setContent: function(str)
19693     {
19694         this.html = str;
19695         this.el.select('.popover-content',true).first().dom.innerHTML = str;
19696     },
19697     // as it get's added to the bottom of the page.
19698     onRender : function(ct, position)
19699     {
19700         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19701         if(!this.el){
19702             var cfg = Roo.apply({},  this.getAutoCreate());
19703             cfg.id = Roo.id();
19704             
19705             if (this.cls) {
19706                 cfg.cls += ' ' + this.cls;
19707             }
19708             if (this.style) {
19709                 cfg.style = this.style;
19710             }
19711             //Roo.log("adding to ");
19712             this.el = Roo.get(document.body).createChild(cfg, position);
19713 //            Roo.log(this.el);
19714         }
19715         this.initEvents();
19716     },
19717     
19718     initEvents : function()
19719     {
19720         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
19721         this.el.enableDisplayMode('block');
19722         this.el.hide();
19723         if (this.over === false) {
19724             return; 
19725         }
19726         if (this.triggers === false) {
19727             return;
19728         }
19729         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19730         var triggers = this.trigger ? this.trigger.split(' ') : [];
19731         Roo.each(triggers, function(trigger) {
19732         
19733             if (trigger == 'click') {
19734                 on_el.on('click', this.toggle, this);
19735             } else if (trigger != 'manual') {
19736                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19737                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19738       
19739                 on_el.on(eventIn  ,this.enter, this);
19740                 on_el.on(eventOut, this.leave, this);
19741             }
19742         }, this);
19743         
19744     },
19745     
19746     
19747     // private
19748     timeout : null,
19749     hoverState : null,
19750     
19751     toggle : function () {
19752         this.hoverState == 'in' ? this.leave() : this.enter();
19753     },
19754     
19755     enter : function () {
19756         
19757         clearTimeout(this.timeout);
19758     
19759         this.hoverState = 'in';
19760     
19761         if (!this.delay || !this.delay.show) {
19762             this.show();
19763             return;
19764         }
19765         var _t = this;
19766         this.timeout = setTimeout(function () {
19767             if (_t.hoverState == 'in') {
19768                 _t.show();
19769             }
19770         }, this.delay.show)
19771     },
19772     
19773     leave : function() {
19774         clearTimeout(this.timeout);
19775     
19776         this.hoverState = 'out';
19777     
19778         if (!this.delay || !this.delay.hide) {
19779             this.hide();
19780             return;
19781         }
19782         var _t = this;
19783         this.timeout = setTimeout(function () {
19784             if (_t.hoverState == 'out') {
19785                 _t.hide();
19786             }
19787         }, this.delay.hide)
19788     },
19789     
19790     show : function (on_el)
19791     {
19792         if (!on_el) {
19793             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19794         }
19795         
19796         // set content.
19797         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
19798         if (this.html !== false) {
19799             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
19800         }
19801         this.el.removeClass([
19802             'fade','top','bottom', 'left', 'right','in',
19803             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19804         ]);
19805         if (!this.title.length) {
19806             this.el.select('.popover-title',true).hide();
19807         }
19808         
19809         var placement = typeof this.placement == 'function' ?
19810             this.placement.call(this, this.el, on_el) :
19811             this.placement;
19812             
19813         var autoToken = /\s?auto?\s?/i;
19814         var autoPlace = autoToken.test(placement);
19815         if (autoPlace) {
19816             placement = placement.replace(autoToken, '') || 'top';
19817         }
19818         
19819         //this.el.detach()
19820         //this.el.setXY([0,0]);
19821         this.el.show();
19822         this.el.dom.style.display='block';
19823         this.el.addClass(placement);
19824         
19825         //this.el.appendTo(on_el);
19826         
19827         var p = this.getPosition();
19828         var box = this.el.getBox();
19829         
19830         if (autoPlace) {
19831             // fixme..
19832         }
19833         var align = Roo.bootstrap.Popover.alignment[placement];
19834         
19835 //        Roo.log(align);
19836         this.el.alignTo(on_el, align[0],align[1]);
19837         //var arrow = this.el.select('.arrow',true).first();
19838         //arrow.set(align[2], 
19839         
19840         this.el.addClass('in');
19841         
19842         
19843         if (this.el.hasClass('fade')) {
19844             // fade it?
19845         }
19846         
19847         this.hoverState = 'in';
19848         
19849         this.fireEvent('show', this);
19850         
19851     },
19852     hide : function()
19853     {
19854         this.el.setXY([0,0]);
19855         this.el.removeClass('in');
19856         this.el.hide();
19857         this.hoverState = null;
19858         
19859         this.fireEvent('hide', this);
19860     }
19861     
19862 });
19863
19864 Roo.bootstrap.Popover.alignment = {
19865     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
19866     'right' : ['l-r', [10,0], 'left bs-popover-left'],
19867     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19868     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19869 };
19870
19871  /*
19872  * - LGPL
19873  *
19874  * Progress
19875  * 
19876  */
19877
19878 /**
19879  * @class Roo.bootstrap.Progress
19880  * @extends Roo.bootstrap.Component
19881  * Bootstrap Progress class
19882  * @cfg {Boolean} striped striped of the progress bar
19883  * @cfg {Boolean} active animated of the progress bar
19884  * 
19885  * 
19886  * @constructor
19887  * Create a new Progress
19888  * @param {Object} config The config object
19889  */
19890
19891 Roo.bootstrap.Progress = function(config){
19892     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
19893 };
19894
19895 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
19896     
19897     striped : false,
19898     active: false,
19899     
19900     getAutoCreate : function(){
19901         var cfg = {
19902             tag: 'div',
19903             cls: 'progress'
19904         };
19905         
19906         
19907         if(this.striped){
19908             cfg.cls += ' progress-striped';
19909         }
19910       
19911         if(this.active){
19912             cfg.cls += ' active';
19913         }
19914         
19915         
19916         return cfg;
19917     }
19918    
19919 });
19920
19921  
19922
19923  /*
19924  * - LGPL
19925  *
19926  * ProgressBar
19927  * 
19928  */
19929
19930 /**
19931  * @class Roo.bootstrap.ProgressBar
19932  * @extends Roo.bootstrap.Component
19933  * Bootstrap ProgressBar class
19934  * @cfg {Number} aria_valuenow aria-value now
19935  * @cfg {Number} aria_valuemin aria-value min
19936  * @cfg {Number} aria_valuemax aria-value max
19937  * @cfg {String} label label for the progress bar
19938  * @cfg {String} panel (success | info | warning | danger )
19939  * @cfg {String} role role of the progress bar
19940  * @cfg {String} sr_only text
19941  * 
19942  * 
19943  * @constructor
19944  * Create a new ProgressBar
19945  * @param {Object} config The config object
19946  */
19947
19948 Roo.bootstrap.ProgressBar = function(config){
19949     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
19950 };
19951
19952 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
19953     
19954     aria_valuenow : 0,
19955     aria_valuemin : 0,
19956     aria_valuemax : 100,
19957     label : false,
19958     panel : false,
19959     role : false,
19960     sr_only: false,
19961     
19962     getAutoCreate : function()
19963     {
19964         
19965         var cfg = {
19966             tag: 'div',
19967             cls: 'progress-bar',
19968             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
19969         };
19970         
19971         if(this.sr_only){
19972             cfg.cn = {
19973                 tag: 'span',
19974                 cls: 'sr-only',
19975                 html: this.sr_only
19976             }
19977         }
19978         
19979         if(this.role){
19980             cfg.role = this.role;
19981         }
19982         
19983         if(this.aria_valuenow){
19984             cfg['aria-valuenow'] = this.aria_valuenow;
19985         }
19986         
19987         if(this.aria_valuemin){
19988             cfg['aria-valuemin'] = this.aria_valuemin;
19989         }
19990         
19991         if(this.aria_valuemax){
19992             cfg['aria-valuemax'] = this.aria_valuemax;
19993         }
19994         
19995         if(this.label && !this.sr_only){
19996             cfg.html = this.label;
19997         }
19998         
19999         if(this.panel){
20000             cfg.cls += ' progress-bar-' + this.panel;
20001         }
20002         
20003         return cfg;
20004     },
20005     
20006     update : function(aria_valuenow)
20007     {
20008         this.aria_valuenow = aria_valuenow;
20009         
20010         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20011     }
20012    
20013 });
20014
20015  
20016
20017  /*
20018  * - LGPL
20019  *
20020  * column
20021  * 
20022  */
20023
20024 /**
20025  * @class Roo.bootstrap.TabGroup
20026  * @extends Roo.bootstrap.Column
20027  * Bootstrap Column class
20028  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20029  * @cfg {Boolean} carousel true to make the group behave like a carousel
20030  * @cfg {Boolean} bullets show bullets for the panels
20031  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20032  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20033  * @cfg {Boolean} showarrow (true|false) show arrow default true
20034  * 
20035  * @constructor
20036  * Create a new TabGroup
20037  * @param {Object} config The config object
20038  */
20039
20040 Roo.bootstrap.TabGroup = function(config){
20041     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20042     if (!this.navId) {
20043         this.navId = Roo.id();
20044     }
20045     this.tabs = [];
20046     Roo.bootstrap.TabGroup.register(this);
20047     
20048 };
20049
20050 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20051     
20052     carousel : false,
20053     transition : false,
20054     bullets : 0,
20055     timer : 0,
20056     autoslide : false,
20057     slideFn : false,
20058     slideOnTouch : false,
20059     showarrow : true,
20060     
20061     getAutoCreate : function()
20062     {
20063         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20064         
20065         cfg.cls += ' tab-content';
20066         
20067         if (this.carousel) {
20068             cfg.cls += ' carousel slide';
20069             
20070             cfg.cn = [{
20071                cls : 'carousel-inner',
20072                cn : []
20073             }];
20074         
20075             if(this.bullets  && !Roo.isTouch){
20076                 
20077                 var bullets = {
20078                     cls : 'carousel-bullets',
20079                     cn : []
20080                 };
20081                
20082                 if(this.bullets_cls){
20083                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20084                 }
20085                 
20086                 bullets.cn.push({
20087                     cls : 'clear'
20088                 });
20089                 
20090                 cfg.cn[0].cn.push(bullets);
20091             }
20092             
20093             if(this.showarrow){
20094                 cfg.cn[0].cn.push({
20095                     tag : 'div',
20096                     class : 'carousel-arrow',
20097                     cn : [
20098                         {
20099                             tag : 'div',
20100                             class : 'carousel-prev',
20101                             cn : [
20102                                 {
20103                                     tag : 'i',
20104                                     class : 'fa fa-chevron-left'
20105                                 }
20106                             ]
20107                         },
20108                         {
20109                             tag : 'div',
20110                             class : 'carousel-next',
20111                             cn : [
20112                                 {
20113                                     tag : 'i',
20114                                     class : 'fa fa-chevron-right'
20115                                 }
20116                             ]
20117                         }
20118                     ]
20119                 });
20120             }
20121             
20122         }
20123         
20124         return cfg;
20125     },
20126     
20127     initEvents:  function()
20128     {
20129 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20130 //            this.el.on("touchstart", this.onTouchStart, this);
20131 //        }
20132         
20133         if(this.autoslide){
20134             var _this = this;
20135             
20136             this.slideFn = window.setInterval(function() {
20137                 _this.showPanelNext();
20138             }, this.timer);
20139         }
20140         
20141         if(this.showarrow){
20142             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20143             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20144         }
20145         
20146         
20147     },
20148     
20149 //    onTouchStart : function(e, el, o)
20150 //    {
20151 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20152 //            return;
20153 //        }
20154 //        
20155 //        this.showPanelNext();
20156 //    },
20157     
20158     
20159     getChildContainer : function()
20160     {
20161         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20162     },
20163     
20164     /**
20165     * register a Navigation item
20166     * @param {Roo.bootstrap.NavItem} the navitem to add
20167     */
20168     register : function(item)
20169     {
20170         this.tabs.push( item);
20171         item.navId = this.navId; // not really needed..
20172         this.addBullet();
20173     
20174     },
20175     
20176     getActivePanel : function()
20177     {
20178         var r = false;
20179         Roo.each(this.tabs, function(t) {
20180             if (t.active) {
20181                 r = t;
20182                 return false;
20183             }
20184             return null;
20185         });
20186         return r;
20187         
20188     },
20189     getPanelByName : function(n)
20190     {
20191         var r = false;
20192         Roo.each(this.tabs, function(t) {
20193             if (t.tabId == n) {
20194                 r = t;
20195                 return false;
20196             }
20197             return null;
20198         });
20199         return r;
20200     },
20201     indexOfPanel : function(p)
20202     {
20203         var r = false;
20204         Roo.each(this.tabs, function(t,i) {
20205             if (t.tabId == p.tabId) {
20206                 r = i;
20207                 return false;
20208             }
20209             return null;
20210         });
20211         return r;
20212     },
20213     /**
20214      * show a specific panel
20215      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20216      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20217      */
20218     showPanel : function (pan)
20219     {
20220         if(this.transition || typeof(pan) == 'undefined'){
20221             Roo.log("waiting for the transitionend");
20222             return false;
20223         }
20224         
20225         if (typeof(pan) == 'number') {
20226             pan = this.tabs[pan];
20227         }
20228         
20229         if (typeof(pan) == 'string') {
20230             pan = this.getPanelByName(pan);
20231         }
20232         
20233         var cur = this.getActivePanel();
20234         
20235         if(!pan || !cur){
20236             Roo.log('pan or acitve pan is undefined');
20237             return false;
20238         }
20239         
20240         if (pan.tabId == this.getActivePanel().tabId) {
20241             return true;
20242         }
20243         
20244         if (false === cur.fireEvent('beforedeactivate')) {
20245             return false;
20246         }
20247         
20248         if(this.bullets > 0 && !Roo.isTouch){
20249             this.setActiveBullet(this.indexOfPanel(pan));
20250         }
20251         
20252         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20253             
20254             //class="carousel-item carousel-item-next carousel-item-left"
20255             
20256             this.transition = true;
20257             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20258             var lr = dir == 'next' ? 'left' : 'right';
20259             pan.el.addClass(dir); // or prev
20260             pan.el.addClass('carousel-item-' + dir); // or prev
20261             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20262             cur.el.addClass(lr); // or right
20263             pan.el.addClass(lr);
20264             cur.el.addClass('carousel-item-' +lr); // or right
20265             pan.el.addClass('carousel-item-' +lr);
20266             
20267             
20268             var _this = this;
20269             cur.el.on('transitionend', function() {
20270                 Roo.log("trans end?");
20271                 
20272                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20273                 pan.setActive(true);
20274                 
20275                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20276                 cur.setActive(false);
20277                 
20278                 _this.transition = false;
20279                 
20280             }, this, { single:  true } );
20281             
20282             return true;
20283         }
20284         
20285         cur.setActive(false);
20286         pan.setActive(true);
20287         
20288         return true;
20289         
20290     },
20291     showPanelNext : function()
20292     {
20293         var i = this.indexOfPanel(this.getActivePanel());
20294         
20295         if (i >= this.tabs.length - 1 && !this.autoslide) {
20296             return;
20297         }
20298         
20299         if (i >= this.tabs.length - 1 && this.autoslide) {
20300             i = -1;
20301         }
20302         
20303         this.showPanel(this.tabs[i+1]);
20304     },
20305     
20306     showPanelPrev : function()
20307     {
20308         var i = this.indexOfPanel(this.getActivePanel());
20309         
20310         if (i  < 1 && !this.autoslide) {
20311             return;
20312         }
20313         
20314         if (i < 1 && this.autoslide) {
20315             i = this.tabs.length;
20316         }
20317         
20318         this.showPanel(this.tabs[i-1]);
20319     },
20320     
20321     
20322     addBullet: function()
20323     {
20324         if(!this.bullets || Roo.isTouch){
20325             return;
20326         }
20327         var ctr = this.el.select('.carousel-bullets',true).first();
20328         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20329         var bullet = ctr.createChild({
20330             cls : 'bullet bullet-' + i
20331         },ctr.dom.lastChild);
20332         
20333         
20334         var _this = this;
20335         
20336         bullet.on('click', (function(e, el, o, ii, t){
20337
20338             e.preventDefault();
20339
20340             this.showPanel(ii);
20341
20342             if(this.autoslide && this.slideFn){
20343                 clearInterval(this.slideFn);
20344                 this.slideFn = window.setInterval(function() {
20345                     _this.showPanelNext();
20346                 }, this.timer);
20347             }
20348
20349         }).createDelegate(this, [i, bullet], true));
20350                 
20351         
20352     },
20353      
20354     setActiveBullet : function(i)
20355     {
20356         if(Roo.isTouch){
20357             return;
20358         }
20359         
20360         Roo.each(this.el.select('.bullet', true).elements, function(el){
20361             el.removeClass('selected');
20362         });
20363
20364         var bullet = this.el.select('.bullet-' + i, true).first();
20365         
20366         if(!bullet){
20367             return;
20368         }
20369         
20370         bullet.addClass('selected');
20371     }
20372     
20373     
20374   
20375 });
20376
20377  
20378
20379  
20380  
20381 Roo.apply(Roo.bootstrap.TabGroup, {
20382     
20383     groups: {},
20384      /**
20385     * register a Navigation Group
20386     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20387     */
20388     register : function(navgrp)
20389     {
20390         this.groups[navgrp.navId] = navgrp;
20391         
20392     },
20393     /**
20394     * fetch a Navigation Group based on the navigation ID
20395     * if one does not exist , it will get created.
20396     * @param {string} the navgroup to add
20397     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20398     */
20399     get: function(navId) {
20400         if (typeof(this.groups[navId]) == 'undefined') {
20401             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20402         }
20403         return this.groups[navId] ;
20404     }
20405     
20406     
20407     
20408 });
20409
20410  /*
20411  * - LGPL
20412  *
20413  * TabPanel
20414  * 
20415  */
20416
20417 /**
20418  * @class Roo.bootstrap.TabPanel
20419  * @extends Roo.bootstrap.Component
20420  * Bootstrap TabPanel class
20421  * @cfg {Boolean} active panel active
20422  * @cfg {String} html panel content
20423  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20424  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20425  * @cfg {String} href click to link..
20426  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20427  * 
20428  * 
20429  * @constructor
20430  * Create a new TabPanel
20431  * @param {Object} config The config object
20432  */
20433
20434 Roo.bootstrap.TabPanel = function(config){
20435     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20436     this.addEvents({
20437         /**
20438              * @event changed
20439              * Fires when the active status changes
20440              * @param {Roo.bootstrap.TabPanel} this
20441              * @param {Boolean} state the new state
20442             
20443          */
20444         'changed': true,
20445         /**
20446              * @event beforedeactivate
20447              * Fires before a tab is de-activated - can be used to do validation on a form.
20448              * @param {Roo.bootstrap.TabPanel} this
20449              * @return {Boolean} false if there is an error
20450             
20451          */
20452         'beforedeactivate': true
20453      });
20454     
20455     this.tabId = this.tabId || Roo.id();
20456   
20457 };
20458
20459 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20460     
20461     active: false,
20462     html: false,
20463     tabId: false,
20464     navId : false,
20465     href : '',
20466     touchSlide : false,
20467     getAutoCreate : function(){
20468         
20469         
20470         var cfg = {
20471             tag: 'div',
20472             // item is needed for carousel - not sure if it has any effect otherwise
20473             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20474             html: this.html || ''
20475         };
20476         
20477         if(this.active){
20478             cfg.cls += ' active';
20479         }
20480         
20481         if(this.tabId){
20482             cfg.tabId = this.tabId;
20483         }
20484         
20485         
20486         
20487         return cfg;
20488     },
20489     
20490     initEvents:  function()
20491     {
20492         var p = this.parent();
20493         
20494         this.navId = this.navId || p.navId;
20495         
20496         if (typeof(this.navId) != 'undefined') {
20497             // not really needed.. but just in case.. parent should be a NavGroup.
20498             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20499             
20500             tg.register(this);
20501             
20502             var i = tg.tabs.length - 1;
20503             
20504             if(this.active && tg.bullets > 0 && i < tg.bullets){
20505                 tg.setActiveBullet(i);
20506             }
20507         }
20508         
20509         this.el.on('click', this.onClick, this);
20510         
20511         if(Roo.isTouch && this.touchSlide){
20512             this.el.on("touchstart", this.onTouchStart, this);
20513             this.el.on("touchmove", this.onTouchMove, this);
20514             this.el.on("touchend", this.onTouchEnd, this);
20515         }
20516         
20517     },
20518     
20519     onRender : function(ct, position)
20520     {
20521         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20522     },
20523     
20524     setActive : function(state)
20525     {
20526         Roo.log("panel - set active " + this.tabId + "=" + state);
20527         
20528         this.active = state;
20529         if (!state) {
20530             this.el.removeClass('active');
20531             
20532         } else  if (!this.el.hasClass('active')) {
20533             this.el.addClass('active');
20534         }
20535         
20536         this.fireEvent('changed', this, state);
20537     },
20538     
20539     onClick : function(e)
20540     {
20541         e.preventDefault();
20542         
20543         if(!this.href.length){
20544             return;
20545         }
20546         
20547         window.location.href = this.href;
20548     },
20549     
20550     startX : 0,
20551     startY : 0,
20552     endX : 0,
20553     endY : 0,
20554     swiping : false,
20555     
20556     onTouchStart : function(e)
20557     {
20558         this.swiping = false;
20559         
20560         this.startX = e.browserEvent.touches[0].clientX;
20561         this.startY = e.browserEvent.touches[0].clientY;
20562     },
20563     
20564     onTouchMove : function(e)
20565     {
20566         this.swiping = true;
20567         
20568         this.endX = e.browserEvent.touches[0].clientX;
20569         this.endY = e.browserEvent.touches[0].clientY;
20570     },
20571     
20572     onTouchEnd : function(e)
20573     {
20574         if(!this.swiping){
20575             this.onClick(e);
20576             return;
20577         }
20578         
20579         var tabGroup = this.parent();
20580         
20581         if(this.endX > this.startX){ // swiping right
20582             tabGroup.showPanelPrev();
20583             return;
20584         }
20585         
20586         if(this.startX > this.endX){ // swiping left
20587             tabGroup.showPanelNext();
20588             return;
20589         }
20590     }
20591     
20592     
20593 });
20594  
20595
20596  
20597
20598  /*
20599  * - LGPL
20600  *
20601  * DateField
20602  * 
20603  */
20604
20605 /**
20606  * @class Roo.bootstrap.DateField
20607  * @extends Roo.bootstrap.Input
20608  * Bootstrap DateField class
20609  * @cfg {Number} weekStart default 0
20610  * @cfg {String} viewMode default empty, (months|years)
20611  * @cfg {String} minViewMode default empty, (months|years)
20612  * @cfg {Number} startDate default -Infinity
20613  * @cfg {Number} endDate default Infinity
20614  * @cfg {Boolean} todayHighlight default false
20615  * @cfg {Boolean} todayBtn default false
20616  * @cfg {Boolean} calendarWeeks default false
20617  * @cfg {Object} daysOfWeekDisabled default empty
20618  * @cfg {Boolean} singleMode default false (true | false)
20619  * 
20620  * @cfg {Boolean} keyboardNavigation default true
20621  * @cfg {String} language default en
20622  * 
20623  * @constructor
20624  * Create a new DateField
20625  * @param {Object} config The config object
20626  */
20627
20628 Roo.bootstrap.DateField = function(config){
20629     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20630      this.addEvents({
20631             /**
20632              * @event show
20633              * Fires when this field show.
20634              * @param {Roo.bootstrap.DateField} this
20635              * @param {Mixed} date The date value
20636              */
20637             show : true,
20638             /**
20639              * @event show
20640              * Fires when this field hide.
20641              * @param {Roo.bootstrap.DateField} this
20642              * @param {Mixed} date The date value
20643              */
20644             hide : true,
20645             /**
20646              * @event select
20647              * Fires when select a date.
20648              * @param {Roo.bootstrap.DateField} this
20649              * @param {Mixed} date The date value
20650              */
20651             select : true,
20652             /**
20653              * @event beforeselect
20654              * Fires when before select a date.
20655              * @param {Roo.bootstrap.DateField} this
20656              * @param {Mixed} date The date value
20657              */
20658             beforeselect : true
20659         });
20660 };
20661
20662 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20663     
20664     /**
20665      * @cfg {String} format
20666      * The default date format string which can be overriden for localization support.  The format must be
20667      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20668      */
20669     format : "m/d/y",
20670     /**
20671      * @cfg {String} altFormats
20672      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20673      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20674      */
20675     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20676     
20677     weekStart : 0,
20678     
20679     viewMode : '',
20680     
20681     minViewMode : '',
20682     
20683     todayHighlight : false,
20684     
20685     todayBtn: false,
20686     
20687     language: 'en',
20688     
20689     keyboardNavigation: true,
20690     
20691     calendarWeeks: false,
20692     
20693     startDate: -Infinity,
20694     
20695     endDate: Infinity,
20696     
20697     daysOfWeekDisabled: [],
20698     
20699     _events: [],
20700     
20701     singleMode : false,
20702     
20703     UTCDate: function()
20704     {
20705         return new Date(Date.UTC.apply(Date, arguments));
20706     },
20707     
20708     UTCToday: function()
20709     {
20710         var today = new Date();
20711         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20712     },
20713     
20714     getDate: function() {
20715             var d = this.getUTCDate();
20716             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20717     },
20718     
20719     getUTCDate: function() {
20720             return this.date;
20721     },
20722     
20723     setDate: function(d) {
20724             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20725     },
20726     
20727     setUTCDate: function(d) {
20728             this.date = d;
20729             this.setValue(this.formatDate(this.date));
20730     },
20731         
20732     onRender: function(ct, position)
20733     {
20734         
20735         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20736         
20737         this.language = this.language || 'en';
20738         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20739         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20740         
20741         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20742         this.format = this.format || 'm/d/y';
20743         this.isInline = false;
20744         this.isInput = true;
20745         this.component = this.el.select('.add-on', true).first() || false;
20746         this.component = (this.component && this.component.length === 0) ? false : this.component;
20747         this.hasInput = this.component && this.inputEl().length;
20748         
20749         if (typeof(this.minViewMode === 'string')) {
20750             switch (this.minViewMode) {
20751                 case 'months':
20752                     this.minViewMode = 1;
20753                     break;
20754                 case 'years':
20755                     this.minViewMode = 2;
20756                     break;
20757                 default:
20758                     this.minViewMode = 0;
20759                     break;
20760             }
20761         }
20762         
20763         if (typeof(this.viewMode === 'string')) {
20764             switch (this.viewMode) {
20765                 case 'months':
20766                     this.viewMode = 1;
20767                     break;
20768                 case 'years':
20769                     this.viewMode = 2;
20770                     break;
20771                 default:
20772                     this.viewMode = 0;
20773                     break;
20774             }
20775         }
20776                 
20777         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20778         
20779 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20780         
20781         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20782         
20783         this.picker().on('mousedown', this.onMousedown, this);
20784         this.picker().on('click', this.onClick, this);
20785         
20786         this.picker().addClass('datepicker-dropdown');
20787         
20788         this.startViewMode = this.viewMode;
20789         
20790         if(this.singleMode){
20791             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
20792                 v.setVisibilityMode(Roo.Element.DISPLAY);
20793                 v.hide();
20794             });
20795             
20796             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20797                 v.setStyle('width', '189px');
20798             });
20799         }
20800         
20801         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
20802             if(!this.calendarWeeks){
20803                 v.remove();
20804                 return;
20805             }
20806             
20807             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20808             v.attr('colspan', function(i, val){
20809                 return parseInt(val) + 1;
20810             });
20811         });
20812                         
20813         
20814         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
20815         
20816         this.setStartDate(this.startDate);
20817         this.setEndDate(this.endDate);
20818         
20819         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
20820         
20821         this.fillDow();
20822         this.fillMonths();
20823         this.update();
20824         this.showMode();
20825         
20826         if(this.isInline) {
20827             this.showPopup();
20828         }
20829     },
20830     
20831     picker : function()
20832     {
20833         return this.pickerEl;
20834 //        return this.el.select('.datepicker', true).first();
20835     },
20836     
20837     fillDow: function()
20838     {
20839         var dowCnt = this.weekStart;
20840         
20841         var dow = {
20842             tag: 'tr',
20843             cn: [
20844                 
20845             ]
20846         };
20847         
20848         if(this.calendarWeeks){
20849             dow.cn.push({
20850                 tag: 'th',
20851                 cls: 'cw',
20852                 html: '&nbsp;'
20853             })
20854         }
20855         
20856         while (dowCnt < this.weekStart + 7) {
20857             dow.cn.push({
20858                 tag: 'th',
20859                 cls: 'dow',
20860                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
20861             });
20862         }
20863         
20864         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
20865     },
20866     
20867     fillMonths: function()
20868     {    
20869         var i = 0;
20870         var months = this.picker().select('>.datepicker-months td', true).first();
20871         
20872         months.dom.innerHTML = '';
20873         
20874         while (i < 12) {
20875             var month = {
20876                 tag: 'span',
20877                 cls: 'month',
20878                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
20879             };
20880             
20881             months.createChild(month);
20882         }
20883         
20884     },
20885     
20886     update: function()
20887     {
20888         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;
20889         
20890         if (this.date < this.startDate) {
20891             this.viewDate = new Date(this.startDate);
20892         } else if (this.date > this.endDate) {
20893             this.viewDate = new Date(this.endDate);
20894         } else {
20895             this.viewDate = new Date(this.date);
20896         }
20897         
20898         this.fill();
20899     },
20900     
20901     fill: function() 
20902     {
20903         var d = new Date(this.viewDate),
20904                 year = d.getUTCFullYear(),
20905                 month = d.getUTCMonth(),
20906                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
20907                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
20908                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
20909                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
20910                 currentDate = this.date && this.date.valueOf(),
20911                 today = this.UTCToday();
20912         
20913         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
20914         
20915 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20916         
20917 //        this.picker.select('>tfoot th.today').
20918 //                                              .text(dates[this.language].today)
20919 //                                              .toggle(this.todayBtn !== false);
20920     
20921         this.updateNavArrows();
20922         this.fillMonths();
20923                                                 
20924         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
20925         
20926         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
20927          
20928         prevMonth.setUTCDate(day);
20929         
20930         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
20931         
20932         var nextMonth = new Date(prevMonth);
20933         
20934         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
20935         
20936         nextMonth = nextMonth.valueOf();
20937         
20938         var fillMonths = false;
20939         
20940         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
20941         
20942         while(prevMonth.valueOf() <= nextMonth) {
20943             var clsName = '';
20944             
20945             if (prevMonth.getUTCDay() === this.weekStart) {
20946                 if(fillMonths){
20947                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
20948                 }
20949                     
20950                 fillMonths = {
20951                     tag: 'tr',
20952                     cn: []
20953                 };
20954                 
20955                 if(this.calendarWeeks){
20956                     // ISO 8601: First week contains first thursday.
20957                     // ISO also states week starts on Monday, but we can be more abstract here.
20958                     var
20959                     // Start of current week: based on weekstart/current date
20960                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
20961                     // Thursday of this week
20962                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
20963                     // First Thursday of year, year from thursday
20964                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
20965                     // Calendar week: ms between thursdays, div ms per day, div 7 days
20966                     calWeek =  (th - yth) / 864e5 / 7 + 1;
20967                     
20968                     fillMonths.cn.push({
20969                         tag: 'td',
20970                         cls: 'cw',
20971                         html: calWeek
20972                     });
20973                 }
20974             }
20975             
20976             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
20977                 clsName += ' old';
20978             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
20979                 clsName += ' new';
20980             }
20981             if (this.todayHighlight &&
20982                 prevMonth.getUTCFullYear() == today.getFullYear() &&
20983                 prevMonth.getUTCMonth() == today.getMonth() &&
20984                 prevMonth.getUTCDate() == today.getDate()) {
20985                 clsName += ' today';
20986             }
20987             
20988             if (currentDate && prevMonth.valueOf() === currentDate) {
20989                 clsName += ' active';
20990             }
20991             
20992             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
20993                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
20994                     clsName += ' disabled';
20995             }
20996             
20997             fillMonths.cn.push({
20998                 tag: 'td',
20999                 cls: 'day ' + clsName,
21000                 html: prevMonth.getDate()
21001             });
21002             
21003             prevMonth.setDate(prevMonth.getDate()+1);
21004         }
21005           
21006         var currentYear = this.date && this.date.getUTCFullYear();
21007         var currentMonth = this.date && this.date.getUTCMonth();
21008         
21009         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21010         
21011         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21012             v.removeClass('active');
21013             
21014             if(currentYear === year && k === currentMonth){
21015                 v.addClass('active');
21016             }
21017             
21018             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21019                 v.addClass('disabled');
21020             }
21021             
21022         });
21023         
21024         
21025         year = parseInt(year/10, 10) * 10;
21026         
21027         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21028         
21029         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21030         
21031         year -= 1;
21032         for (var i = -1; i < 11; i++) {
21033             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21034                 tag: 'span',
21035                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21036                 html: year
21037             });
21038             
21039             year += 1;
21040         }
21041     },
21042     
21043     showMode: function(dir) 
21044     {
21045         if (dir) {
21046             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21047         }
21048         
21049         Roo.each(this.picker().select('>div',true).elements, function(v){
21050             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21051             v.hide();
21052         });
21053         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21054     },
21055     
21056     place: function()
21057     {
21058         if(this.isInline) {
21059             return;
21060         }
21061         
21062         this.picker().removeClass(['bottom', 'top']);
21063         
21064         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21065             /*
21066              * place to the top of element!
21067              *
21068              */
21069             
21070             this.picker().addClass('top');
21071             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21072             
21073             return;
21074         }
21075         
21076         this.picker().addClass('bottom');
21077         
21078         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21079     },
21080     
21081     parseDate : function(value)
21082     {
21083         if(!value || value instanceof Date){
21084             return value;
21085         }
21086         var v = Date.parseDate(value, this.format);
21087         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21088             v = Date.parseDate(value, 'Y-m-d');
21089         }
21090         if(!v && this.altFormats){
21091             if(!this.altFormatsArray){
21092                 this.altFormatsArray = this.altFormats.split("|");
21093             }
21094             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21095                 v = Date.parseDate(value, this.altFormatsArray[i]);
21096             }
21097         }
21098         return v;
21099     },
21100     
21101     formatDate : function(date, fmt)
21102     {   
21103         return (!date || !(date instanceof Date)) ?
21104         date : date.dateFormat(fmt || this.format);
21105     },
21106     
21107     onFocus : function()
21108     {
21109         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21110         this.showPopup();
21111     },
21112     
21113     onBlur : function()
21114     {
21115         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21116         
21117         var d = this.inputEl().getValue();
21118         
21119         this.setValue(d);
21120                 
21121         this.hidePopup();
21122     },
21123     
21124     showPopup : function()
21125     {
21126         this.picker().show();
21127         this.update();
21128         this.place();
21129         
21130         this.fireEvent('showpopup', this, this.date);
21131     },
21132     
21133     hidePopup : function()
21134     {
21135         if(this.isInline) {
21136             return;
21137         }
21138         this.picker().hide();
21139         this.viewMode = this.startViewMode;
21140         this.showMode();
21141         
21142         this.fireEvent('hidepopup', this, this.date);
21143         
21144     },
21145     
21146     onMousedown: function(e)
21147     {
21148         e.stopPropagation();
21149         e.preventDefault();
21150     },
21151     
21152     keyup: function(e)
21153     {
21154         Roo.bootstrap.DateField.superclass.keyup.call(this);
21155         this.update();
21156     },
21157
21158     setValue: function(v)
21159     {
21160         if(this.fireEvent('beforeselect', this, v) !== false){
21161             var d = new Date(this.parseDate(v) ).clearTime();
21162         
21163             if(isNaN(d.getTime())){
21164                 this.date = this.viewDate = '';
21165                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21166                 return;
21167             }
21168
21169             v = this.formatDate(d);
21170
21171             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21172
21173             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21174
21175             this.update();
21176
21177             this.fireEvent('select', this, this.date);
21178         }
21179     },
21180     
21181     getValue: function()
21182     {
21183         return this.formatDate(this.date);
21184     },
21185     
21186     fireKey: function(e)
21187     {
21188         if (!this.picker().isVisible()){
21189             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21190                 this.showPopup();
21191             }
21192             return;
21193         }
21194         
21195         var dateChanged = false,
21196         dir, day, month,
21197         newDate, newViewDate;
21198         
21199         switch(e.keyCode){
21200             case 27: // escape
21201                 this.hidePopup();
21202                 e.preventDefault();
21203                 break;
21204             case 37: // left
21205             case 39: // right
21206                 if (!this.keyboardNavigation) {
21207                     break;
21208                 }
21209                 dir = e.keyCode == 37 ? -1 : 1;
21210                 
21211                 if (e.ctrlKey){
21212                     newDate = this.moveYear(this.date, dir);
21213                     newViewDate = this.moveYear(this.viewDate, dir);
21214                 } else if (e.shiftKey){
21215                     newDate = this.moveMonth(this.date, dir);
21216                     newViewDate = this.moveMonth(this.viewDate, dir);
21217                 } else {
21218                     newDate = new Date(this.date);
21219                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21220                     newViewDate = new Date(this.viewDate);
21221                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21222                 }
21223                 if (this.dateWithinRange(newDate)){
21224                     this.date = newDate;
21225                     this.viewDate = newViewDate;
21226                     this.setValue(this.formatDate(this.date));
21227 //                    this.update();
21228                     e.preventDefault();
21229                     dateChanged = true;
21230                 }
21231                 break;
21232             case 38: // up
21233             case 40: // down
21234                 if (!this.keyboardNavigation) {
21235                     break;
21236                 }
21237                 dir = e.keyCode == 38 ? -1 : 1;
21238                 if (e.ctrlKey){
21239                     newDate = this.moveYear(this.date, dir);
21240                     newViewDate = this.moveYear(this.viewDate, dir);
21241                 } else if (e.shiftKey){
21242                     newDate = this.moveMonth(this.date, dir);
21243                     newViewDate = this.moveMonth(this.viewDate, dir);
21244                 } else {
21245                     newDate = new Date(this.date);
21246                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21247                     newViewDate = new Date(this.viewDate);
21248                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21249                 }
21250                 if (this.dateWithinRange(newDate)){
21251                     this.date = newDate;
21252                     this.viewDate = newViewDate;
21253                     this.setValue(this.formatDate(this.date));
21254 //                    this.update();
21255                     e.preventDefault();
21256                     dateChanged = true;
21257                 }
21258                 break;
21259             case 13: // enter
21260                 this.setValue(this.formatDate(this.date));
21261                 this.hidePopup();
21262                 e.preventDefault();
21263                 break;
21264             case 9: // tab
21265                 this.setValue(this.formatDate(this.date));
21266                 this.hidePopup();
21267                 break;
21268             case 16: // shift
21269             case 17: // ctrl
21270             case 18: // alt
21271                 break;
21272             default :
21273                 this.hidePopup();
21274                 
21275         }
21276     },
21277     
21278     
21279     onClick: function(e) 
21280     {
21281         e.stopPropagation();
21282         e.preventDefault();
21283         
21284         var target = e.getTarget();
21285         
21286         if(target.nodeName.toLowerCase() === 'i'){
21287             target = Roo.get(target).dom.parentNode;
21288         }
21289         
21290         var nodeName = target.nodeName;
21291         var className = target.className;
21292         var html = target.innerHTML;
21293         //Roo.log(nodeName);
21294         
21295         switch(nodeName.toLowerCase()) {
21296             case 'th':
21297                 switch(className) {
21298                     case 'switch':
21299                         this.showMode(1);
21300                         break;
21301                     case 'prev':
21302                     case 'next':
21303                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21304                         switch(this.viewMode){
21305                                 case 0:
21306                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21307                                         break;
21308                                 case 1:
21309                                 case 2:
21310                                         this.viewDate = this.moveYear(this.viewDate, dir);
21311                                         break;
21312                         }
21313                         this.fill();
21314                         break;
21315                     case 'today':
21316                         var date = new Date();
21317                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21318 //                        this.fill()
21319                         this.setValue(this.formatDate(this.date));
21320                         
21321                         this.hidePopup();
21322                         break;
21323                 }
21324                 break;
21325             case 'span':
21326                 if (className.indexOf('disabled') < 0) {
21327                     this.viewDate.setUTCDate(1);
21328                     if (className.indexOf('month') > -1) {
21329                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21330                     } else {
21331                         var year = parseInt(html, 10) || 0;
21332                         this.viewDate.setUTCFullYear(year);
21333                         
21334                     }
21335                     
21336                     if(this.singleMode){
21337                         this.setValue(this.formatDate(this.viewDate));
21338                         this.hidePopup();
21339                         return;
21340                     }
21341                     
21342                     this.showMode(-1);
21343                     this.fill();
21344                 }
21345                 break;
21346                 
21347             case 'td':
21348                 //Roo.log(className);
21349                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21350                     var day = parseInt(html, 10) || 1;
21351                     var year = this.viewDate.getUTCFullYear(),
21352                         month = this.viewDate.getUTCMonth();
21353
21354                     if (className.indexOf('old') > -1) {
21355                         if(month === 0 ){
21356                             month = 11;
21357                             year -= 1;
21358                         }else{
21359                             month -= 1;
21360                         }
21361                     } else if (className.indexOf('new') > -1) {
21362                         if (month == 11) {
21363                             month = 0;
21364                             year += 1;
21365                         } else {
21366                             month += 1;
21367                         }
21368                     }
21369                     //Roo.log([year,month,day]);
21370                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21371                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21372 //                    this.fill();
21373                     //Roo.log(this.formatDate(this.date));
21374                     this.setValue(this.formatDate(this.date));
21375                     this.hidePopup();
21376                 }
21377                 break;
21378         }
21379     },
21380     
21381     setStartDate: function(startDate)
21382     {
21383         this.startDate = startDate || -Infinity;
21384         if (this.startDate !== -Infinity) {
21385             this.startDate = this.parseDate(this.startDate);
21386         }
21387         this.update();
21388         this.updateNavArrows();
21389     },
21390
21391     setEndDate: function(endDate)
21392     {
21393         this.endDate = endDate || Infinity;
21394         if (this.endDate !== Infinity) {
21395             this.endDate = this.parseDate(this.endDate);
21396         }
21397         this.update();
21398         this.updateNavArrows();
21399     },
21400     
21401     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21402     {
21403         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21404         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21405             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21406         }
21407         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21408             return parseInt(d, 10);
21409         });
21410         this.update();
21411         this.updateNavArrows();
21412     },
21413     
21414     updateNavArrows: function() 
21415     {
21416         if(this.singleMode){
21417             return;
21418         }
21419         
21420         var d = new Date(this.viewDate),
21421         year = d.getUTCFullYear(),
21422         month = d.getUTCMonth();
21423         
21424         Roo.each(this.picker().select('.prev', true).elements, function(v){
21425             v.show();
21426             switch (this.viewMode) {
21427                 case 0:
21428
21429                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21430                         v.hide();
21431                     }
21432                     break;
21433                 case 1:
21434                 case 2:
21435                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21436                         v.hide();
21437                     }
21438                     break;
21439             }
21440         });
21441         
21442         Roo.each(this.picker().select('.next', true).elements, function(v){
21443             v.show();
21444             switch (this.viewMode) {
21445                 case 0:
21446
21447                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21448                         v.hide();
21449                     }
21450                     break;
21451                 case 1:
21452                 case 2:
21453                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21454                         v.hide();
21455                     }
21456                     break;
21457             }
21458         })
21459     },
21460     
21461     moveMonth: function(date, dir)
21462     {
21463         if (!dir) {
21464             return date;
21465         }
21466         var new_date = new Date(date.valueOf()),
21467         day = new_date.getUTCDate(),
21468         month = new_date.getUTCMonth(),
21469         mag = Math.abs(dir),
21470         new_month, test;
21471         dir = dir > 0 ? 1 : -1;
21472         if (mag == 1){
21473             test = dir == -1
21474             // If going back one month, make sure month is not current month
21475             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21476             ? function(){
21477                 return new_date.getUTCMonth() == month;
21478             }
21479             // If going forward one month, make sure month is as expected
21480             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21481             : function(){
21482                 return new_date.getUTCMonth() != new_month;
21483             };
21484             new_month = month + dir;
21485             new_date.setUTCMonth(new_month);
21486             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21487             if (new_month < 0 || new_month > 11) {
21488                 new_month = (new_month + 12) % 12;
21489             }
21490         } else {
21491             // For magnitudes >1, move one month at a time...
21492             for (var i=0; i<mag; i++) {
21493                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21494                 new_date = this.moveMonth(new_date, dir);
21495             }
21496             // ...then reset the day, keeping it in the new month
21497             new_month = new_date.getUTCMonth();
21498             new_date.setUTCDate(day);
21499             test = function(){
21500                 return new_month != new_date.getUTCMonth();
21501             };
21502         }
21503         // Common date-resetting loop -- if date is beyond end of month, make it
21504         // end of month
21505         while (test()){
21506             new_date.setUTCDate(--day);
21507             new_date.setUTCMonth(new_month);
21508         }
21509         return new_date;
21510     },
21511
21512     moveYear: function(date, dir)
21513     {
21514         return this.moveMonth(date, dir*12);
21515     },
21516
21517     dateWithinRange: function(date)
21518     {
21519         return date >= this.startDate && date <= this.endDate;
21520     },
21521
21522     
21523     remove: function() 
21524     {
21525         this.picker().remove();
21526     },
21527     
21528     validateValue : function(value)
21529     {
21530         if(this.getVisibilityEl().hasClass('hidden')){
21531             return true;
21532         }
21533         
21534         if(value.length < 1)  {
21535             if(this.allowBlank){
21536                 return true;
21537             }
21538             return false;
21539         }
21540         
21541         if(value.length < this.minLength){
21542             return false;
21543         }
21544         if(value.length > this.maxLength){
21545             return false;
21546         }
21547         if(this.vtype){
21548             var vt = Roo.form.VTypes;
21549             if(!vt[this.vtype](value, this)){
21550                 return false;
21551             }
21552         }
21553         if(typeof this.validator == "function"){
21554             var msg = this.validator(value);
21555             if(msg !== true){
21556                 return false;
21557             }
21558         }
21559         
21560         if(this.regex && !this.regex.test(value)){
21561             return false;
21562         }
21563         
21564         if(typeof(this.parseDate(value)) == 'undefined'){
21565             return false;
21566         }
21567         
21568         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21569             return false;
21570         }      
21571         
21572         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21573             return false;
21574         } 
21575         
21576         
21577         return true;
21578     },
21579     
21580     reset : function()
21581     {
21582         this.date = this.viewDate = '';
21583         
21584         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21585     }
21586    
21587 });
21588
21589 Roo.apply(Roo.bootstrap.DateField,  {
21590     
21591     head : {
21592         tag: 'thead',
21593         cn: [
21594         {
21595             tag: 'tr',
21596             cn: [
21597             {
21598                 tag: 'th',
21599                 cls: 'prev',
21600                 html: '<i class="fa fa-arrow-left"/>'
21601             },
21602             {
21603                 tag: 'th',
21604                 cls: 'switch',
21605                 colspan: '5'
21606             },
21607             {
21608                 tag: 'th',
21609                 cls: 'next',
21610                 html: '<i class="fa fa-arrow-right"/>'
21611             }
21612
21613             ]
21614         }
21615         ]
21616     },
21617     
21618     content : {
21619         tag: 'tbody',
21620         cn: [
21621         {
21622             tag: 'tr',
21623             cn: [
21624             {
21625                 tag: 'td',
21626                 colspan: '7'
21627             }
21628             ]
21629         }
21630         ]
21631     },
21632     
21633     footer : {
21634         tag: 'tfoot',
21635         cn: [
21636         {
21637             tag: 'tr',
21638             cn: [
21639             {
21640                 tag: 'th',
21641                 colspan: '7',
21642                 cls: 'today'
21643             }
21644                     
21645             ]
21646         }
21647         ]
21648     },
21649     
21650     dates:{
21651         en: {
21652             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21653             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21654             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21655             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21656             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21657             today: "Today"
21658         }
21659     },
21660     
21661     modes: [
21662     {
21663         clsName: 'days',
21664         navFnc: 'Month',
21665         navStep: 1
21666     },
21667     {
21668         clsName: 'months',
21669         navFnc: 'FullYear',
21670         navStep: 1
21671     },
21672     {
21673         clsName: 'years',
21674         navFnc: 'FullYear',
21675         navStep: 10
21676     }]
21677 });
21678
21679 Roo.apply(Roo.bootstrap.DateField,  {
21680   
21681     template : {
21682         tag: 'div',
21683         cls: 'datepicker dropdown-menu roo-dynamic shadow',
21684         cn: [
21685         {
21686             tag: 'div',
21687             cls: 'datepicker-days',
21688             cn: [
21689             {
21690                 tag: 'table',
21691                 cls: 'table-condensed',
21692                 cn:[
21693                 Roo.bootstrap.DateField.head,
21694                 {
21695                     tag: 'tbody'
21696                 },
21697                 Roo.bootstrap.DateField.footer
21698                 ]
21699             }
21700             ]
21701         },
21702         {
21703             tag: 'div',
21704             cls: 'datepicker-months',
21705             cn: [
21706             {
21707                 tag: 'table',
21708                 cls: 'table-condensed',
21709                 cn:[
21710                 Roo.bootstrap.DateField.head,
21711                 Roo.bootstrap.DateField.content,
21712                 Roo.bootstrap.DateField.footer
21713                 ]
21714             }
21715             ]
21716         },
21717         {
21718             tag: 'div',
21719             cls: 'datepicker-years',
21720             cn: [
21721             {
21722                 tag: 'table',
21723                 cls: 'table-condensed',
21724                 cn:[
21725                 Roo.bootstrap.DateField.head,
21726                 Roo.bootstrap.DateField.content,
21727                 Roo.bootstrap.DateField.footer
21728                 ]
21729             }
21730             ]
21731         }
21732         ]
21733     }
21734 });
21735
21736  
21737
21738  /*
21739  * - LGPL
21740  *
21741  * TimeField
21742  * 
21743  */
21744
21745 /**
21746  * @class Roo.bootstrap.TimeField
21747  * @extends Roo.bootstrap.Input
21748  * Bootstrap DateField class
21749  * 
21750  * 
21751  * @constructor
21752  * Create a new TimeField
21753  * @param {Object} config The config object
21754  */
21755
21756 Roo.bootstrap.TimeField = function(config){
21757     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21758     this.addEvents({
21759             /**
21760              * @event show
21761              * Fires when this field show.
21762              * @param {Roo.bootstrap.DateField} thisthis
21763              * @param {Mixed} date The date value
21764              */
21765             show : true,
21766             /**
21767              * @event show
21768              * Fires when this field hide.
21769              * @param {Roo.bootstrap.DateField} this
21770              * @param {Mixed} date The date value
21771              */
21772             hide : true,
21773             /**
21774              * @event select
21775              * Fires when select a date.
21776              * @param {Roo.bootstrap.DateField} this
21777              * @param {Mixed} date The date value
21778              */
21779             select : true
21780         });
21781 };
21782
21783 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
21784     
21785     /**
21786      * @cfg {String} format
21787      * The default time format string which can be overriden for localization support.  The format must be
21788      * valid according to {@link Date#parseDate} (defaults to 'H:i').
21789      */
21790     format : "H:i",
21791        
21792     onRender: function(ct, position)
21793     {
21794         
21795         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
21796                 
21797         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
21798         
21799         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21800         
21801         this.pop = this.picker().select('>.datepicker-time',true).first();
21802         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21803         
21804         this.picker().on('mousedown', this.onMousedown, this);
21805         this.picker().on('click', this.onClick, this);
21806         
21807         this.picker().addClass('datepicker-dropdown');
21808     
21809         this.fillTime();
21810         this.update();
21811             
21812         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
21813         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
21814         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
21815         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
21816         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
21817         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
21818
21819     },
21820     
21821     fireKey: function(e){
21822         if (!this.picker().isVisible()){
21823             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21824                 this.show();
21825             }
21826             return;
21827         }
21828
21829         e.preventDefault();
21830         
21831         switch(e.keyCode){
21832             case 27: // escape
21833                 this.hide();
21834                 break;
21835             case 37: // left
21836             case 39: // right
21837                 this.onTogglePeriod();
21838                 break;
21839             case 38: // up
21840                 this.onIncrementMinutes();
21841                 break;
21842             case 40: // down
21843                 this.onDecrementMinutes();
21844                 break;
21845             case 13: // enter
21846             case 9: // tab
21847                 this.setTime();
21848                 break;
21849         }
21850     },
21851     
21852     onClick: function(e) {
21853         e.stopPropagation();
21854         e.preventDefault();
21855     },
21856     
21857     picker : function()
21858     {
21859         return this.el.select('.datepicker', true).first();
21860     },
21861     
21862     fillTime: function()
21863     {    
21864         var time = this.pop.select('tbody', true).first();
21865         
21866         time.dom.innerHTML = '';
21867         
21868         time.createChild({
21869             tag: 'tr',
21870             cn: [
21871                 {
21872                     tag: 'td',
21873                     cn: [
21874                         {
21875                             tag: 'a',
21876                             href: '#',
21877                             cls: 'btn',
21878                             cn: [
21879                                 {
21880                                     tag: 'span',
21881                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
21882                                 }
21883                             ]
21884                         } 
21885                     ]
21886                 },
21887                 {
21888                     tag: 'td',
21889                     cls: 'separator'
21890                 },
21891                 {
21892                     tag: 'td',
21893                     cn: [
21894                         {
21895                             tag: 'a',
21896                             href: '#',
21897                             cls: 'btn',
21898                             cn: [
21899                                 {
21900                                     tag: 'span',
21901                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
21902                                 }
21903                             ]
21904                         }
21905                     ]
21906                 },
21907                 {
21908                     tag: 'td',
21909                     cls: 'separator'
21910                 }
21911             ]
21912         });
21913         
21914         time.createChild({
21915             tag: 'tr',
21916             cn: [
21917                 {
21918                     tag: 'td',
21919                     cn: [
21920                         {
21921                             tag: 'span',
21922                             cls: 'timepicker-hour',
21923                             html: '00'
21924                         }  
21925                     ]
21926                 },
21927                 {
21928                     tag: 'td',
21929                     cls: 'separator',
21930                     html: ':'
21931                 },
21932                 {
21933                     tag: 'td',
21934                     cn: [
21935                         {
21936                             tag: 'span',
21937                             cls: 'timepicker-minute',
21938                             html: '00'
21939                         }  
21940                     ]
21941                 },
21942                 {
21943                     tag: 'td',
21944                     cls: 'separator'
21945                 },
21946                 {
21947                     tag: 'td',
21948                     cn: [
21949                         {
21950                             tag: 'button',
21951                             type: 'button',
21952                             cls: 'btn btn-primary period',
21953                             html: 'AM'
21954                             
21955                         }
21956                     ]
21957                 }
21958             ]
21959         });
21960         
21961         time.createChild({
21962             tag: 'tr',
21963             cn: [
21964                 {
21965                     tag: 'td',
21966                     cn: [
21967                         {
21968                             tag: 'a',
21969                             href: '#',
21970                             cls: 'btn',
21971                             cn: [
21972                                 {
21973                                     tag: 'span',
21974                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
21975                                 }
21976                             ]
21977                         }
21978                     ]
21979                 },
21980                 {
21981                     tag: 'td',
21982                     cls: 'separator'
21983                 },
21984                 {
21985                     tag: 'td',
21986                     cn: [
21987                         {
21988                             tag: 'a',
21989                             href: '#',
21990                             cls: 'btn',
21991                             cn: [
21992                                 {
21993                                     tag: 'span',
21994                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
21995                                 }
21996                             ]
21997                         }
21998                     ]
21999                 },
22000                 {
22001                     tag: 'td',
22002                     cls: 'separator'
22003                 }
22004             ]
22005         });
22006         
22007     },
22008     
22009     update: function()
22010     {
22011         
22012         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22013         
22014         this.fill();
22015     },
22016     
22017     fill: function() 
22018     {
22019         var hours = this.time.getHours();
22020         var minutes = this.time.getMinutes();
22021         var period = 'AM';
22022         
22023         if(hours > 11){
22024             period = 'PM';
22025         }
22026         
22027         if(hours == 0){
22028             hours = 12;
22029         }
22030         
22031         
22032         if(hours > 12){
22033             hours = hours - 12;
22034         }
22035         
22036         if(hours < 10){
22037             hours = '0' + hours;
22038         }
22039         
22040         if(minutes < 10){
22041             minutes = '0' + minutes;
22042         }
22043         
22044         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22045         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22046         this.pop.select('button', true).first().dom.innerHTML = period;
22047         
22048     },
22049     
22050     place: function()
22051     {   
22052         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22053         
22054         var cls = ['bottom'];
22055         
22056         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22057             cls.pop();
22058             cls.push('top');
22059         }
22060         
22061         cls.push('right');
22062         
22063         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22064             cls.pop();
22065             cls.push('left');
22066         }
22067         
22068         this.picker().addClass(cls.join('-'));
22069         
22070         var _this = this;
22071         
22072         Roo.each(cls, function(c){
22073             if(c == 'bottom'){
22074                 _this.picker().setTop(_this.inputEl().getHeight());
22075                 return;
22076             }
22077             if(c == 'top'){
22078                 _this.picker().setTop(0 - _this.picker().getHeight());
22079                 return;
22080             }
22081             
22082             if(c == 'left'){
22083                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22084                 return;
22085             }
22086             if(c == 'right'){
22087                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22088                 return;
22089             }
22090         });
22091         
22092     },
22093   
22094     onFocus : function()
22095     {
22096         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22097         this.show();
22098     },
22099     
22100     onBlur : function()
22101     {
22102         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22103         this.hide();
22104     },
22105     
22106     show : function()
22107     {
22108         this.picker().show();
22109         this.pop.show();
22110         this.update();
22111         this.place();
22112         
22113         this.fireEvent('show', this, this.date);
22114     },
22115     
22116     hide : function()
22117     {
22118         this.picker().hide();
22119         this.pop.hide();
22120         
22121         this.fireEvent('hide', this, this.date);
22122     },
22123     
22124     setTime : function()
22125     {
22126         this.hide();
22127         this.setValue(this.time.format(this.format));
22128         
22129         this.fireEvent('select', this, this.date);
22130         
22131         
22132     },
22133     
22134     onMousedown: function(e){
22135         e.stopPropagation();
22136         e.preventDefault();
22137     },
22138     
22139     onIncrementHours: function()
22140     {
22141         Roo.log('onIncrementHours');
22142         this.time = this.time.add(Date.HOUR, 1);
22143         this.update();
22144         
22145     },
22146     
22147     onDecrementHours: function()
22148     {
22149         Roo.log('onDecrementHours');
22150         this.time = this.time.add(Date.HOUR, -1);
22151         this.update();
22152     },
22153     
22154     onIncrementMinutes: function()
22155     {
22156         Roo.log('onIncrementMinutes');
22157         this.time = this.time.add(Date.MINUTE, 1);
22158         this.update();
22159     },
22160     
22161     onDecrementMinutes: function()
22162     {
22163         Roo.log('onDecrementMinutes');
22164         this.time = this.time.add(Date.MINUTE, -1);
22165         this.update();
22166     },
22167     
22168     onTogglePeriod: function()
22169     {
22170         Roo.log('onTogglePeriod');
22171         this.time = this.time.add(Date.HOUR, 12);
22172         this.update();
22173     }
22174     
22175    
22176 });
22177
22178 Roo.apply(Roo.bootstrap.TimeField,  {
22179     
22180     content : {
22181         tag: 'tbody',
22182         cn: [
22183             {
22184                 tag: 'tr',
22185                 cn: [
22186                 {
22187                     tag: 'td',
22188                     colspan: '7'
22189                 }
22190                 ]
22191             }
22192         ]
22193     },
22194     
22195     footer : {
22196         tag: 'tfoot',
22197         cn: [
22198             {
22199                 tag: 'tr',
22200                 cn: [
22201                 {
22202                     tag: 'th',
22203                     colspan: '7',
22204                     cls: '',
22205                     cn: [
22206                         {
22207                             tag: 'button',
22208                             cls: 'btn btn-info ok',
22209                             html: 'OK'
22210                         }
22211                     ]
22212                 }
22213
22214                 ]
22215             }
22216         ]
22217     }
22218 });
22219
22220 Roo.apply(Roo.bootstrap.TimeField,  {
22221   
22222     template : {
22223         tag: 'div',
22224         cls: 'datepicker dropdown-menu',
22225         cn: [
22226             {
22227                 tag: 'div',
22228                 cls: 'datepicker-time',
22229                 cn: [
22230                 {
22231                     tag: 'table',
22232                     cls: 'table-condensed',
22233                     cn:[
22234                     Roo.bootstrap.TimeField.content,
22235                     Roo.bootstrap.TimeField.footer
22236                     ]
22237                 }
22238                 ]
22239             }
22240         ]
22241     }
22242 });
22243
22244  
22245
22246  /*
22247  * - LGPL
22248  *
22249  * MonthField
22250  * 
22251  */
22252
22253 /**
22254  * @class Roo.bootstrap.MonthField
22255  * @extends Roo.bootstrap.Input
22256  * Bootstrap MonthField class
22257  * 
22258  * @cfg {String} language default en
22259  * 
22260  * @constructor
22261  * Create a new MonthField
22262  * @param {Object} config The config object
22263  */
22264
22265 Roo.bootstrap.MonthField = function(config){
22266     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22267     
22268     this.addEvents({
22269         /**
22270          * @event show
22271          * Fires when this field show.
22272          * @param {Roo.bootstrap.MonthField} this
22273          * @param {Mixed} date The date value
22274          */
22275         show : true,
22276         /**
22277          * @event show
22278          * Fires when this field hide.
22279          * @param {Roo.bootstrap.MonthField} this
22280          * @param {Mixed} date The date value
22281          */
22282         hide : true,
22283         /**
22284          * @event select
22285          * Fires when select a date.
22286          * @param {Roo.bootstrap.MonthField} this
22287          * @param {String} oldvalue The old value
22288          * @param {String} newvalue The new value
22289          */
22290         select : true
22291     });
22292 };
22293
22294 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22295     
22296     onRender: function(ct, position)
22297     {
22298         
22299         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22300         
22301         this.language = this.language || 'en';
22302         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22303         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22304         
22305         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22306         this.isInline = false;
22307         this.isInput = true;
22308         this.component = this.el.select('.add-on', true).first() || false;
22309         this.component = (this.component && this.component.length === 0) ? false : this.component;
22310         this.hasInput = this.component && this.inputEL().length;
22311         
22312         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22313         
22314         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22315         
22316         this.picker().on('mousedown', this.onMousedown, this);
22317         this.picker().on('click', this.onClick, this);
22318         
22319         this.picker().addClass('datepicker-dropdown');
22320         
22321         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22322             v.setStyle('width', '189px');
22323         });
22324         
22325         this.fillMonths();
22326         
22327         this.update();
22328         
22329         if(this.isInline) {
22330             this.show();
22331         }
22332         
22333     },
22334     
22335     setValue: function(v, suppressEvent)
22336     {   
22337         var o = this.getValue();
22338         
22339         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22340         
22341         this.update();
22342
22343         if(suppressEvent !== true){
22344             this.fireEvent('select', this, o, v);
22345         }
22346         
22347     },
22348     
22349     getValue: function()
22350     {
22351         return this.value;
22352     },
22353     
22354     onClick: function(e) 
22355     {
22356         e.stopPropagation();
22357         e.preventDefault();
22358         
22359         var target = e.getTarget();
22360         
22361         if(target.nodeName.toLowerCase() === 'i'){
22362             target = Roo.get(target).dom.parentNode;
22363         }
22364         
22365         var nodeName = target.nodeName;
22366         var className = target.className;
22367         var html = target.innerHTML;
22368         
22369         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22370             return;
22371         }
22372         
22373         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22374         
22375         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22376         
22377         this.hide();
22378                         
22379     },
22380     
22381     picker : function()
22382     {
22383         return this.pickerEl;
22384     },
22385     
22386     fillMonths: function()
22387     {    
22388         var i = 0;
22389         var months = this.picker().select('>.datepicker-months td', true).first();
22390         
22391         months.dom.innerHTML = '';
22392         
22393         while (i < 12) {
22394             var month = {
22395                 tag: 'span',
22396                 cls: 'month',
22397                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22398             };
22399             
22400             months.createChild(month);
22401         }
22402         
22403     },
22404     
22405     update: function()
22406     {
22407         var _this = this;
22408         
22409         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22410             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22411         }
22412         
22413         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22414             e.removeClass('active');
22415             
22416             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22417                 e.addClass('active');
22418             }
22419         })
22420     },
22421     
22422     place: function()
22423     {
22424         if(this.isInline) {
22425             return;
22426         }
22427         
22428         this.picker().removeClass(['bottom', 'top']);
22429         
22430         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22431             /*
22432              * place to the top of element!
22433              *
22434              */
22435             
22436             this.picker().addClass('top');
22437             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22438             
22439             return;
22440         }
22441         
22442         this.picker().addClass('bottom');
22443         
22444         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22445     },
22446     
22447     onFocus : function()
22448     {
22449         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22450         this.show();
22451     },
22452     
22453     onBlur : function()
22454     {
22455         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22456         
22457         var d = this.inputEl().getValue();
22458         
22459         this.setValue(d);
22460                 
22461         this.hide();
22462     },
22463     
22464     show : function()
22465     {
22466         this.picker().show();
22467         this.picker().select('>.datepicker-months', true).first().show();
22468         this.update();
22469         this.place();
22470         
22471         this.fireEvent('show', this, this.date);
22472     },
22473     
22474     hide : function()
22475     {
22476         if(this.isInline) {
22477             return;
22478         }
22479         this.picker().hide();
22480         this.fireEvent('hide', this, this.date);
22481         
22482     },
22483     
22484     onMousedown: function(e)
22485     {
22486         e.stopPropagation();
22487         e.preventDefault();
22488     },
22489     
22490     keyup: function(e)
22491     {
22492         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22493         this.update();
22494     },
22495
22496     fireKey: function(e)
22497     {
22498         if (!this.picker().isVisible()){
22499             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22500                 this.show();
22501             }
22502             return;
22503         }
22504         
22505         var dir;
22506         
22507         switch(e.keyCode){
22508             case 27: // escape
22509                 this.hide();
22510                 e.preventDefault();
22511                 break;
22512             case 37: // left
22513             case 39: // right
22514                 dir = e.keyCode == 37 ? -1 : 1;
22515                 
22516                 this.vIndex = this.vIndex + dir;
22517                 
22518                 if(this.vIndex < 0){
22519                     this.vIndex = 0;
22520                 }
22521                 
22522                 if(this.vIndex > 11){
22523                     this.vIndex = 11;
22524                 }
22525                 
22526                 if(isNaN(this.vIndex)){
22527                     this.vIndex = 0;
22528                 }
22529                 
22530                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22531                 
22532                 break;
22533             case 38: // up
22534             case 40: // down
22535                 
22536                 dir = e.keyCode == 38 ? -1 : 1;
22537                 
22538                 this.vIndex = this.vIndex + dir * 4;
22539                 
22540                 if(this.vIndex < 0){
22541                     this.vIndex = 0;
22542                 }
22543                 
22544                 if(this.vIndex > 11){
22545                     this.vIndex = 11;
22546                 }
22547                 
22548                 if(isNaN(this.vIndex)){
22549                     this.vIndex = 0;
22550                 }
22551                 
22552                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22553                 break;
22554                 
22555             case 13: // enter
22556                 
22557                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22558                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22559                 }
22560                 
22561                 this.hide();
22562                 e.preventDefault();
22563                 break;
22564             case 9: // tab
22565                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22566                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22567                 }
22568                 this.hide();
22569                 break;
22570             case 16: // shift
22571             case 17: // ctrl
22572             case 18: // alt
22573                 break;
22574             default :
22575                 this.hide();
22576                 
22577         }
22578     },
22579     
22580     remove: function() 
22581     {
22582         this.picker().remove();
22583     }
22584    
22585 });
22586
22587 Roo.apply(Roo.bootstrap.MonthField,  {
22588     
22589     content : {
22590         tag: 'tbody',
22591         cn: [
22592         {
22593             tag: 'tr',
22594             cn: [
22595             {
22596                 tag: 'td',
22597                 colspan: '7'
22598             }
22599             ]
22600         }
22601         ]
22602     },
22603     
22604     dates:{
22605         en: {
22606             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22607             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22608         }
22609     }
22610 });
22611
22612 Roo.apply(Roo.bootstrap.MonthField,  {
22613   
22614     template : {
22615         tag: 'div',
22616         cls: 'datepicker dropdown-menu roo-dynamic',
22617         cn: [
22618             {
22619                 tag: 'div',
22620                 cls: 'datepicker-months',
22621                 cn: [
22622                 {
22623                     tag: 'table',
22624                     cls: 'table-condensed',
22625                     cn:[
22626                         Roo.bootstrap.DateField.content
22627                     ]
22628                 }
22629                 ]
22630             }
22631         ]
22632     }
22633 });
22634
22635  
22636
22637  
22638  /*
22639  * - LGPL
22640  *
22641  * CheckBox
22642  * 
22643  */
22644
22645 /**
22646  * @class Roo.bootstrap.CheckBox
22647  * @extends Roo.bootstrap.Input
22648  * Bootstrap CheckBox class
22649  * 
22650  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22651  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22652  * @cfg {String} boxLabel The text that appears beside the checkbox
22653  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22654  * @cfg {Boolean} checked initnal the element
22655  * @cfg {Boolean} inline inline the element (default false)
22656  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22657  * @cfg {String} tooltip label tooltip
22658  * 
22659  * @constructor
22660  * Create a new CheckBox
22661  * @param {Object} config The config object
22662  */
22663
22664 Roo.bootstrap.CheckBox = function(config){
22665     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22666    
22667     this.addEvents({
22668         /**
22669         * @event check
22670         * Fires when the element is checked or unchecked.
22671         * @param {Roo.bootstrap.CheckBox} this This input
22672         * @param {Boolean} checked The new checked value
22673         */
22674        check : true,
22675        /**
22676         * @event click
22677         * Fires when the element is click.
22678         * @param {Roo.bootstrap.CheckBox} this This input
22679         */
22680        click : true
22681     });
22682     
22683 };
22684
22685 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
22686   
22687     inputType: 'checkbox',
22688     inputValue: 1,
22689     valueOff: 0,
22690     boxLabel: false,
22691     checked: false,
22692     weight : false,
22693     inline: false,
22694     tooltip : '',
22695     
22696     // checkbox success does not make any sense really.. 
22697     invalidClass : "",
22698     validClass : "",
22699     
22700     
22701     getAutoCreate : function()
22702     {
22703         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22704         
22705         var id = Roo.id();
22706         
22707         var cfg = {};
22708         
22709         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22710         
22711         if(this.inline){
22712             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
22713         }
22714         
22715         var input =  {
22716             tag: 'input',
22717             id : id,
22718             type : this.inputType,
22719             value : this.inputValue,
22720             cls : 'roo-' + this.inputType, //'form-box',
22721             placeholder : this.placeholder || ''
22722             
22723         };
22724         
22725         if(this.inputType != 'radio'){
22726             var hidden =  {
22727                 tag: 'input',
22728                 type : 'hidden',
22729                 cls : 'roo-hidden-value',
22730                 value : this.checked ? this.inputValue : this.valueOff
22731             };
22732         }
22733         
22734             
22735         if (this.weight) { // Validity check?
22736             cfg.cls += " " + this.inputType + "-" + this.weight;
22737         }
22738         
22739         if (this.disabled) {
22740             input.disabled=true;
22741         }
22742         
22743         if(this.checked){
22744             input.checked = this.checked;
22745         }
22746         
22747         if (this.name) {
22748             
22749             input.name = this.name;
22750             
22751             if(this.inputType != 'radio'){
22752                 hidden.name = this.name;
22753                 input.name = '_hidden_' + this.name;
22754             }
22755         }
22756         
22757         if (this.size) {
22758             input.cls += ' input-' + this.size;
22759         }
22760         
22761         var settings=this;
22762         
22763         ['xs','sm','md','lg'].map(function(size){
22764             if (settings[size]) {
22765                 cfg.cls += ' col-' + size + '-' + settings[size];
22766             }
22767         });
22768         
22769         var inputblock = input;
22770          
22771         if (this.before || this.after) {
22772             
22773             inputblock = {
22774                 cls : 'input-group',
22775                 cn :  [] 
22776             };
22777             
22778             if (this.before) {
22779                 inputblock.cn.push({
22780                     tag :'span',
22781                     cls : 'input-group-addon',
22782                     html : this.before
22783                 });
22784             }
22785             
22786             inputblock.cn.push(input);
22787             
22788             if(this.inputType != 'radio'){
22789                 inputblock.cn.push(hidden);
22790             }
22791             
22792             if (this.after) {
22793                 inputblock.cn.push({
22794                     tag :'span',
22795                     cls : 'input-group-addon',
22796                     html : this.after
22797                 });
22798             }
22799             
22800         }
22801         var boxLabelCfg = false;
22802         
22803         if(this.boxLabel){
22804            
22805             boxLabelCfg = {
22806                 tag: 'label',
22807                 //'for': id, // box label is handled by onclick - so no for...
22808                 cls: 'box-label',
22809                 html: this.boxLabel
22810             };
22811             if(this.tooltip){
22812                 boxLabelCfg.tooltip = this.tooltip;
22813             }
22814              
22815         }
22816         
22817         
22818         if (align ==='left' && this.fieldLabel.length) {
22819 //                Roo.log("left and has label");
22820             cfg.cn = [
22821                 {
22822                     tag: 'label',
22823                     'for' :  id,
22824                     cls : 'control-label',
22825                     html : this.fieldLabel
22826                 },
22827                 {
22828                     cls : "", 
22829                     cn: [
22830                         inputblock
22831                     ]
22832                 }
22833             ];
22834             
22835             if (boxLabelCfg) {
22836                 cfg.cn[1].cn.push(boxLabelCfg);
22837             }
22838             
22839             if(this.labelWidth > 12){
22840                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
22841             }
22842             
22843             if(this.labelWidth < 13 && this.labelmd == 0){
22844                 this.labelmd = this.labelWidth;
22845             }
22846             
22847             if(this.labellg > 0){
22848                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
22849                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
22850             }
22851             
22852             if(this.labelmd > 0){
22853                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
22854                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
22855             }
22856             
22857             if(this.labelsm > 0){
22858                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
22859                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
22860             }
22861             
22862             if(this.labelxs > 0){
22863                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
22864                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
22865             }
22866             
22867         } else if ( this.fieldLabel.length) {
22868 //                Roo.log(" label");
22869                 cfg.cn = [
22870                    
22871                     {
22872                         tag: this.boxLabel ? 'span' : 'label',
22873                         'for': id,
22874                         cls: 'control-label box-input-label',
22875                         //cls : 'input-group-addon',
22876                         html : this.fieldLabel
22877                     },
22878                     
22879                     inputblock
22880                     
22881                 ];
22882                 if (boxLabelCfg) {
22883                     cfg.cn.push(boxLabelCfg);
22884                 }
22885
22886         } else {
22887             
22888 //                Roo.log(" no label && no align");
22889                 cfg.cn = [  inputblock ] ;
22890                 if (boxLabelCfg) {
22891                     cfg.cn.push(boxLabelCfg);
22892                 }
22893
22894                 
22895         }
22896         
22897        
22898         
22899         if(this.inputType != 'radio'){
22900             cfg.cn.push(hidden);
22901         }
22902         
22903         return cfg;
22904         
22905     },
22906     
22907     /**
22908      * return the real input element.
22909      */
22910     inputEl: function ()
22911     {
22912         return this.el.select('input.roo-' + this.inputType,true).first();
22913     },
22914     hiddenEl: function ()
22915     {
22916         return this.el.select('input.roo-hidden-value',true).first();
22917     },
22918     
22919     labelEl: function()
22920     {
22921         return this.el.select('label.control-label',true).first();
22922     },
22923     /* depricated... */
22924     
22925     label: function()
22926     {
22927         return this.labelEl();
22928     },
22929     
22930     boxLabelEl: function()
22931     {
22932         return this.el.select('label.box-label',true).first();
22933     },
22934     
22935     initEvents : function()
22936     {
22937 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
22938         
22939         this.inputEl().on('click', this.onClick,  this);
22940         
22941         if (this.boxLabel) { 
22942             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
22943         }
22944         
22945         this.startValue = this.getValue();
22946         
22947         if(this.groupId){
22948             Roo.bootstrap.CheckBox.register(this);
22949         }
22950     },
22951     
22952     onClick : function(e)
22953     {   
22954         if(this.fireEvent('click', this, e) !== false){
22955             this.setChecked(!this.checked);
22956         }
22957         
22958     },
22959     
22960     setChecked : function(state,suppressEvent)
22961     {
22962         this.startValue = this.getValue();
22963
22964         if(this.inputType == 'radio'){
22965             
22966             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22967                 e.dom.checked = false;
22968             });
22969             
22970             this.inputEl().dom.checked = true;
22971             
22972             this.inputEl().dom.value = this.inputValue;
22973             
22974             if(suppressEvent !== true){
22975                 this.fireEvent('check', this, true);
22976             }
22977             
22978             this.validate();
22979             
22980             return;
22981         }
22982         
22983         this.checked = state;
22984         
22985         this.inputEl().dom.checked = state;
22986         
22987         
22988         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
22989         
22990         if(suppressEvent !== true){
22991             this.fireEvent('check', this, state);
22992         }
22993         
22994         this.validate();
22995     },
22996     
22997     getValue : function()
22998     {
22999         if(this.inputType == 'radio'){
23000             return this.getGroupValue();
23001         }
23002         
23003         return this.hiddenEl().dom.value;
23004         
23005     },
23006     
23007     getGroupValue : function()
23008     {
23009         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23010             return '';
23011         }
23012         
23013         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23014     },
23015     
23016     setValue : function(v,suppressEvent)
23017     {
23018         if(this.inputType == 'radio'){
23019             this.setGroupValue(v, suppressEvent);
23020             return;
23021         }
23022         
23023         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23024         
23025         this.validate();
23026     },
23027     
23028     setGroupValue : function(v, suppressEvent)
23029     {
23030         this.startValue = this.getValue();
23031         
23032         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23033             e.dom.checked = false;
23034             
23035             if(e.dom.value == v){
23036                 e.dom.checked = true;
23037             }
23038         });
23039         
23040         if(suppressEvent !== true){
23041             this.fireEvent('check', this, true);
23042         }
23043
23044         this.validate();
23045         
23046         return;
23047     },
23048     
23049     validate : function()
23050     {
23051         if(this.getVisibilityEl().hasClass('hidden')){
23052             return true;
23053         }
23054         
23055         if(
23056                 this.disabled || 
23057                 (this.inputType == 'radio' && this.validateRadio()) ||
23058                 (this.inputType == 'checkbox' && this.validateCheckbox())
23059         ){
23060             this.markValid();
23061             return true;
23062         }
23063         
23064         this.markInvalid();
23065         return false;
23066     },
23067     
23068     validateRadio : function()
23069     {
23070         if(this.getVisibilityEl().hasClass('hidden')){
23071             return true;
23072         }
23073         
23074         if(this.allowBlank){
23075             return true;
23076         }
23077         
23078         var valid = false;
23079         
23080         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23081             if(!e.dom.checked){
23082                 return;
23083             }
23084             
23085             valid = true;
23086             
23087             return false;
23088         });
23089         
23090         return valid;
23091     },
23092     
23093     validateCheckbox : function()
23094     {
23095         if(!this.groupId){
23096             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23097             //return (this.getValue() == this.inputValue) ? true : false;
23098         }
23099         
23100         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23101         
23102         if(!group){
23103             return false;
23104         }
23105         
23106         var r = false;
23107         
23108         for(var i in group){
23109             if(group[i].el.isVisible(true)){
23110                 r = false;
23111                 break;
23112             }
23113             
23114             r = true;
23115         }
23116         
23117         for(var i in group){
23118             if(r){
23119                 break;
23120             }
23121             
23122             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23123         }
23124         
23125         return r;
23126     },
23127     
23128     /**
23129      * Mark this field as valid
23130      */
23131     markValid : function()
23132     {
23133         var _this = this;
23134         
23135         this.fireEvent('valid', this);
23136         
23137         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23138         
23139         if(this.groupId){
23140             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23141         }
23142         
23143         if(label){
23144             label.markValid();
23145         }
23146
23147         if(this.inputType == 'radio'){
23148             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23149                 var fg = e.findParent('.form-group', false, true);
23150                 if (Roo.bootstrap.version == 3) {
23151                     fg.removeClass([_this.invalidClass, _this.validClass]);
23152                     fg.addClass(_this.validClass);
23153                 } else {
23154                     fg.removeClass(['is-valid', 'is-invalid']);
23155                     fg.addClass('is-valid');
23156                 }
23157             });
23158             
23159             return;
23160         }
23161
23162         if(!this.groupId){
23163             var fg = this.el.findParent('.form-group', false, true);
23164             if (Roo.bootstrap.version == 3) {
23165                 fg.removeClass([this.invalidClass, this.validClass]);
23166                 fg.addClass(this.validClass);
23167             } else {
23168                 fg.removeClass(['is-valid', 'is-invalid']);
23169                 fg.addClass('is-valid');
23170             }
23171             return;
23172         }
23173         
23174         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23175         
23176         if(!group){
23177             return;
23178         }
23179         
23180         for(var i in group){
23181             var fg = group[i].el.findParent('.form-group', false, true);
23182             if (Roo.bootstrap.version == 3) {
23183                 fg.removeClass([this.invalidClass, this.validClass]);
23184                 fg.addClass(this.validClass);
23185             } else {
23186                 fg.removeClass(['is-valid', 'is-invalid']);
23187                 fg.addClass('is-valid');
23188             }
23189         }
23190     },
23191     
23192      /**
23193      * Mark this field as invalid
23194      * @param {String} msg The validation message
23195      */
23196     markInvalid : function(msg)
23197     {
23198         if(this.allowBlank){
23199             return;
23200         }
23201         
23202         var _this = this;
23203         
23204         this.fireEvent('invalid', this, msg);
23205         
23206         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23207         
23208         if(this.groupId){
23209             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23210         }
23211         
23212         if(label){
23213             label.markInvalid();
23214         }
23215             
23216         if(this.inputType == 'radio'){
23217             
23218             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23219                 var fg = e.findParent('.form-group', false, true);
23220                 if (Roo.bootstrap.version == 3) {
23221                     fg.removeClass([_this.invalidClass, _this.validClass]);
23222                     fg.addClass(_this.invalidClass);
23223                 } else {
23224                     fg.removeClass(['is-invalid', 'is-valid']);
23225                     fg.addClass('is-invalid');
23226                 }
23227             });
23228             
23229             return;
23230         }
23231         
23232         if(!this.groupId){
23233             var fg = this.el.findParent('.form-group', false, true);
23234             if (Roo.bootstrap.version == 3) {
23235                 fg.removeClass([_this.invalidClass, _this.validClass]);
23236                 fg.addClass(_this.invalidClass);
23237             } else {
23238                 fg.removeClass(['is-invalid', 'is-valid']);
23239                 fg.addClass('is-invalid');
23240             }
23241             return;
23242         }
23243         
23244         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23245         
23246         if(!group){
23247             return;
23248         }
23249         
23250         for(var i in group){
23251             var fg = group[i].el.findParent('.form-group', false, true);
23252             if (Roo.bootstrap.version == 3) {
23253                 fg.removeClass([_this.invalidClass, _this.validClass]);
23254                 fg.addClass(_this.invalidClass);
23255             } else {
23256                 fg.removeClass(['is-invalid', 'is-valid']);
23257                 fg.addClass('is-invalid');
23258             }
23259         }
23260         
23261     },
23262     
23263     clearInvalid : function()
23264     {
23265         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23266         
23267         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23268         
23269         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23270         
23271         if (label && label.iconEl) {
23272             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23273             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23274         }
23275     },
23276     
23277     disable : function()
23278     {
23279         if(this.inputType != 'radio'){
23280             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23281             return;
23282         }
23283         
23284         var _this = this;
23285         
23286         if(this.rendered){
23287             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23288                 _this.getActionEl().addClass(this.disabledClass);
23289                 e.dom.disabled = true;
23290             });
23291         }
23292         
23293         this.disabled = true;
23294         this.fireEvent("disable", this);
23295         return this;
23296     },
23297
23298     enable : function()
23299     {
23300         if(this.inputType != 'radio'){
23301             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23302             return;
23303         }
23304         
23305         var _this = this;
23306         
23307         if(this.rendered){
23308             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23309                 _this.getActionEl().removeClass(this.disabledClass);
23310                 e.dom.disabled = false;
23311             });
23312         }
23313         
23314         this.disabled = false;
23315         this.fireEvent("enable", this);
23316         return this;
23317     },
23318     
23319     setBoxLabel : function(v)
23320     {
23321         this.boxLabel = v;
23322         
23323         if(this.rendered){
23324             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23325         }
23326     }
23327
23328 });
23329
23330 Roo.apply(Roo.bootstrap.CheckBox, {
23331     
23332     groups: {},
23333     
23334      /**
23335     * register a CheckBox Group
23336     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23337     */
23338     register : function(checkbox)
23339     {
23340         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23341             this.groups[checkbox.groupId] = {};
23342         }
23343         
23344         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23345             return;
23346         }
23347         
23348         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23349         
23350     },
23351     /**
23352     * fetch a CheckBox Group based on the group ID
23353     * @param {string} the group ID
23354     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23355     */
23356     get: function(groupId) {
23357         if (typeof(this.groups[groupId]) == 'undefined') {
23358             return false;
23359         }
23360         
23361         return this.groups[groupId] ;
23362     }
23363     
23364     
23365 });
23366 /*
23367  * - LGPL
23368  *
23369  * RadioItem
23370  * 
23371  */
23372
23373 /**
23374  * @class Roo.bootstrap.Radio
23375  * @extends Roo.bootstrap.Component
23376  * Bootstrap Radio class
23377  * @cfg {String} boxLabel - the label associated
23378  * @cfg {String} value - the value of radio
23379  * 
23380  * @constructor
23381  * Create a new Radio
23382  * @param {Object} config The config object
23383  */
23384 Roo.bootstrap.Radio = function(config){
23385     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23386     
23387 };
23388
23389 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23390     
23391     boxLabel : '',
23392     
23393     value : '',
23394     
23395     getAutoCreate : function()
23396     {
23397         var cfg = {
23398             tag : 'div',
23399             cls : 'form-group radio',
23400             cn : [
23401                 {
23402                     tag : 'label',
23403                     cls : 'box-label',
23404                     html : this.boxLabel
23405                 }
23406             ]
23407         };
23408         
23409         return cfg;
23410     },
23411     
23412     initEvents : function() 
23413     {
23414         this.parent().register(this);
23415         
23416         this.el.on('click', this.onClick, this);
23417         
23418     },
23419     
23420     onClick : function(e)
23421     {
23422         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23423             this.setChecked(true);
23424         }
23425     },
23426     
23427     setChecked : function(state, suppressEvent)
23428     {
23429         this.parent().setValue(this.value, suppressEvent);
23430         
23431     },
23432     
23433     setBoxLabel : function(v)
23434     {
23435         this.boxLabel = v;
23436         
23437         if(this.rendered){
23438             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23439         }
23440     }
23441     
23442 });
23443  
23444
23445  /*
23446  * - LGPL
23447  *
23448  * Input
23449  * 
23450  */
23451
23452 /**
23453  * @class Roo.bootstrap.SecurePass
23454  * @extends Roo.bootstrap.Input
23455  * Bootstrap SecurePass class
23456  *
23457  * 
23458  * @constructor
23459  * Create a new SecurePass
23460  * @param {Object} config The config object
23461  */
23462  
23463 Roo.bootstrap.SecurePass = function (config) {
23464     // these go here, so the translation tool can replace them..
23465     this.errors = {
23466         PwdEmpty: "Please type a password, and then retype it to confirm.",
23467         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23468         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23469         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23470         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23471         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23472         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23473         TooWeak: "Your password is Too Weak."
23474     },
23475     this.meterLabel = "Password strength:";
23476     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23477     this.meterClass = [
23478         "roo-password-meter-tooweak", 
23479         "roo-password-meter-weak", 
23480         "roo-password-meter-medium", 
23481         "roo-password-meter-strong", 
23482         "roo-password-meter-grey"
23483     ];
23484     
23485     this.errors = {};
23486     
23487     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23488 }
23489
23490 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23491     /**
23492      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23493      * {
23494      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23495      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23496      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23497      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23498      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23499      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23500      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23501      * })
23502      */
23503     // private
23504     
23505     meterWidth: 300,
23506     errorMsg :'',    
23507     errors: false,
23508     imageRoot: '/',
23509     /**
23510      * @cfg {String/Object} Label for the strength meter (defaults to
23511      * 'Password strength:')
23512      */
23513     // private
23514     meterLabel: '',
23515     /**
23516      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23517      * ['Weak', 'Medium', 'Strong'])
23518      */
23519     // private    
23520     pwdStrengths: false,    
23521     // private
23522     strength: 0,
23523     // private
23524     _lastPwd: null,
23525     // private
23526     kCapitalLetter: 0,
23527     kSmallLetter: 1,
23528     kDigit: 2,
23529     kPunctuation: 3,
23530     
23531     insecure: false,
23532     // private
23533     initEvents: function ()
23534     {
23535         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23536
23537         if (this.el.is('input[type=password]') && Roo.isSafari) {
23538             this.el.on('keydown', this.SafariOnKeyDown, this);
23539         }
23540
23541         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23542     },
23543     // private
23544     onRender: function (ct, position)
23545     {
23546         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23547         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23548         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23549
23550         this.trigger.createChild({
23551                    cn: [
23552                     {
23553                     //id: 'PwdMeter',
23554                     tag: 'div',
23555                     cls: 'roo-password-meter-grey col-xs-12',
23556                     style: {
23557                         //width: 0,
23558                         //width: this.meterWidth + 'px'                                                
23559                         }
23560                     },
23561                     {                            
23562                          cls: 'roo-password-meter-text'                          
23563                     }
23564                 ]            
23565         });
23566
23567          
23568         if (this.hideTrigger) {
23569             this.trigger.setDisplayed(false);
23570         }
23571         this.setSize(this.width || '', this.height || '');
23572     },
23573     // private
23574     onDestroy: function ()
23575     {
23576         if (this.trigger) {
23577             this.trigger.removeAllListeners();
23578             this.trigger.remove();
23579         }
23580         if (this.wrap) {
23581             this.wrap.remove();
23582         }
23583         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23584     },
23585     // private
23586     checkStrength: function ()
23587     {
23588         var pwd = this.inputEl().getValue();
23589         if (pwd == this._lastPwd) {
23590             return;
23591         }
23592
23593         var strength;
23594         if (this.ClientSideStrongPassword(pwd)) {
23595             strength = 3;
23596         } else if (this.ClientSideMediumPassword(pwd)) {
23597             strength = 2;
23598         } else if (this.ClientSideWeakPassword(pwd)) {
23599             strength = 1;
23600         } else {
23601             strength = 0;
23602         }
23603         
23604         Roo.log('strength1: ' + strength);
23605         
23606         //var pm = this.trigger.child('div/div/div').dom;
23607         var pm = this.trigger.child('div/div');
23608         pm.removeClass(this.meterClass);
23609         pm.addClass(this.meterClass[strength]);
23610                 
23611         
23612         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23613                 
23614         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23615         
23616         this._lastPwd = pwd;
23617     },
23618     reset: function ()
23619     {
23620         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23621         
23622         this._lastPwd = '';
23623         
23624         var pm = this.trigger.child('div/div');
23625         pm.removeClass(this.meterClass);
23626         pm.addClass('roo-password-meter-grey');        
23627         
23628         
23629         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23630         
23631         pt.innerHTML = '';
23632         this.inputEl().dom.type='password';
23633     },
23634     // private
23635     validateValue: function (value)
23636     {
23637         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23638             return false;
23639         }
23640         if (value.length == 0) {
23641             if (this.allowBlank) {
23642                 this.clearInvalid();
23643                 return true;
23644             }
23645
23646             this.markInvalid(this.errors.PwdEmpty);
23647             this.errorMsg = this.errors.PwdEmpty;
23648             return false;
23649         }
23650         
23651         if(this.insecure){
23652             return true;
23653         }
23654         
23655         if (!value.match(/[\x21-\x7e]+/)) {
23656             this.markInvalid(this.errors.PwdBadChar);
23657             this.errorMsg = this.errors.PwdBadChar;
23658             return false;
23659         }
23660         if (value.length < 6) {
23661             this.markInvalid(this.errors.PwdShort);
23662             this.errorMsg = this.errors.PwdShort;
23663             return false;
23664         }
23665         if (value.length > 16) {
23666             this.markInvalid(this.errors.PwdLong);
23667             this.errorMsg = this.errors.PwdLong;
23668             return false;
23669         }
23670         var strength;
23671         if (this.ClientSideStrongPassword(value)) {
23672             strength = 3;
23673         } else if (this.ClientSideMediumPassword(value)) {
23674             strength = 2;
23675         } else if (this.ClientSideWeakPassword(value)) {
23676             strength = 1;
23677         } else {
23678             strength = 0;
23679         }
23680
23681         
23682         if (strength < 2) {
23683             //this.markInvalid(this.errors.TooWeak);
23684             this.errorMsg = this.errors.TooWeak;
23685             //return false;
23686         }
23687         
23688         
23689         console.log('strength2: ' + strength);
23690         
23691         //var pm = this.trigger.child('div/div/div').dom;
23692         
23693         var pm = this.trigger.child('div/div');
23694         pm.removeClass(this.meterClass);
23695         pm.addClass(this.meterClass[strength]);
23696                 
23697         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23698                 
23699         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23700         
23701         this.errorMsg = ''; 
23702         return true;
23703     },
23704     // private
23705     CharacterSetChecks: function (type)
23706     {
23707         this.type = type;
23708         this.fResult = false;
23709     },
23710     // private
23711     isctype: function (character, type)
23712     {
23713         switch (type) {  
23714             case this.kCapitalLetter:
23715                 if (character >= 'A' && character <= 'Z') {
23716                     return true;
23717                 }
23718                 break;
23719             
23720             case this.kSmallLetter:
23721                 if (character >= 'a' && character <= 'z') {
23722                     return true;
23723                 }
23724                 break;
23725             
23726             case this.kDigit:
23727                 if (character >= '0' && character <= '9') {
23728                     return true;
23729                 }
23730                 break;
23731             
23732             case this.kPunctuation:
23733                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23734                     return true;
23735                 }
23736                 break;
23737             
23738             default:
23739                 return false;
23740         }
23741
23742     },
23743     // private
23744     IsLongEnough: function (pwd, size)
23745     {
23746         return !(pwd == null || isNaN(size) || pwd.length < size);
23747     },
23748     // private
23749     SpansEnoughCharacterSets: function (word, nb)
23750     {
23751         if (!this.IsLongEnough(word, nb))
23752         {
23753             return false;
23754         }
23755
23756         var characterSetChecks = new Array(
23757             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23758             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23759         );
23760         
23761         for (var index = 0; index < word.length; ++index) {
23762             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23763                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23764                     characterSetChecks[nCharSet].fResult = true;
23765                     break;
23766                 }
23767             }
23768         }
23769
23770         var nCharSets = 0;
23771         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23772             if (characterSetChecks[nCharSet].fResult) {
23773                 ++nCharSets;
23774             }
23775         }
23776
23777         if (nCharSets < nb) {
23778             return false;
23779         }
23780         return true;
23781     },
23782     // private
23783     ClientSideStrongPassword: function (pwd)
23784     {
23785         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23786     },
23787     // private
23788     ClientSideMediumPassword: function (pwd)
23789     {
23790         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
23791     },
23792     // private
23793     ClientSideWeakPassword: function (pwd)
23794     {
23795         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
23796     }
23797           
23798 })//<script type="text/javascript">
23799
23800 /*
23801  * Based  Ext JS Library 1.1.1
23802  * Copyright(c) 2006-2007, Ext JS, LLC.
23803  * LGPL
23804  *
23805  */
23806  
23807 /**
23808  * @class Roo.HtmlEditorCore
23809  * @extends Roo.Component
23810  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23811  *
23812  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23813  */
23814
23815 Roo.HtmlEditorCore = function(config){
23816     
23817     
23818     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
23819     
23820     
23821     this.addEvents({
23822         /**
23823          * @event initialize
23824          * Fires when the editor is fully initialized (including the iframe)
23825          * @param {Roo.HtmlEditorCore} this
23826          */
23827         initialize: true,
23828         /**
23829          * @event activate
23830          * Fires when the editor is first receives the focus. Any insertion must wait
23831          * until after this event.
23832          * @param {Roo.HtmlEditorCore} this
23833          */
23834         activate: true,
23835          /**
23836          * @event beforesync
23837          * Fires before the textarea is updated with content from the editor iframe. Return false
23838          * to cancel the sync.
23839          * @param {Roo.HtmlEditorCore} this
23840          * @param {String} html
23841          */
23842         beforesync: true,
23843          /**
23844          * @event beforepush
23845          * Fires before the iframe editor is updated with content from the textarea. Return false
23846          * to cancel the push.
23847          * @param {Roo.HtmlEditorCore} this
23848          * @param {String} html
23849          */
23850         beforepush: true,
23851          /**
23852          * @event sync
23853          * Fires when the textarea is updated with content from the editor iframe.
23854          * @param {Roo.HtmlEditorCore} this
23855          * @param {String} html
23856          */
23857         sync: true,
23858          /**
23859          * @event push
23860          * Fires when the iframe editor is updated with content from the textarea.
23861          * @param {Roo.HtmlEditorCore} this
23862          * @param {String} html
23863          */
23864         push: true,
23865         
23866         /**
23867          * @event editorevent
23868          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23869          * @param {Roo.HtmlEditorCore} this
23870          */
23871         editorevent: true
23872         
23873     });
23874     
23875     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
23876     
23877     // defaults : white / black...
23878     this.applyBlacklists();
23879     
23880     
23881     
23882 };
23883
23884
23885 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
23886
23887
23888      /**
23889      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
23890      */
23891     
23892     owner : false,
23893     
23894      /**
23895      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23896      *                        Roo.resizable.
23897      */
23898     resizable : false,
23899      /**
23900      * @cfg {Number} height (in pixels)
23901      */   
23902     height: 300,
23903    /**
23904      * @cfg {Number} width (in pixels)
23905      */   
23906     width: 500,
23907     
23908     /**
23909      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23910      * 
23911      */
23912     stylesheets: false,
23913     
23914     // id of frame..
23915     frameId: false,
23916     
23917     // private properties
23918     validationEvent : false,
23919     deferHeight: true,
23920     initialized : false,
23921     activated : false,
23922     sourceEditMode : false,
23923     onFocus : Roo.emptyFn,
23924     iframePad:3,
23925     hideMode:'offsets',
23926     
23927     clearUp: true,
23928     
23929     // blacklist + whitelisted elements..
23930     black: false,
23931     white: false,
23932      
23933     bodyCls : '',
23934
23935     /**
23936      * Protected method that will not generally be called directly. It
23937      * is called when the editor initializes the iframe with HTML contents. Override this method if you
23938      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
23939      */
23940     getDocMarkup : function(){
23941         // body styles..
23942         var st = '';
23943         
23944         // inherit styels from page...?? 
23945         if (this.stylesheets === false) {
23946             
23947             Roo.get(document.head).select('style').each(function(node) {
23948                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23949             });
23950             
23951             Roo.get(document.head).select('link').each(function(node) { 
23952                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23953             });
23954             
23955         } else if (!this.stylesheets.length) {
23956                 // simple..
23957                 st = '<style type="text/css">' +
23958                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23959                    '</style>';
23960         } else {
23961             for (var i in this.stylesheets) { 
23962                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
23963             }
23964             
23965         }
23966         
23967         st +=  '<style type="text/css">' +
23968             'IMG { cursor: pointer } ' +
23969         '</style>';
23970
23971         var cls = 'roo-htmleditor-body';
23972         
23973         if(this.bodyCls.length){
23974             cls += ' ' + this.bodyCls;
23975         }
23976         
23977         return '<html><head>' + st  +
23978             //<style type="text/css">' +
23979             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23980             //'</style>' +
23981             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
23982     },
23983
23984     // private
23985     onRender : function(ct, position)
23986     {
23987         var _t = this;
23988         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
23989         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
23990         
23991         
23992         this.el.dom.style.border = '0 none';
23993         this.el.dom.setAttribute('tabIndex', -1);
23994         this.el.addClass('x-hidden hide');
23995         
23996         
23997         
23998         if(Roo.isIE){ // fix IE 1px bogus margin
23999             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24000         }
24001        
24002         
24003         this.frameId = Roo.id();
24004         
24005          
24006         
24007         var iframe = this.owner.wrap.createChild({
24008             tag: 'iframe',
24009             cls: 'form-control', // bootstrap..
24010             id: this.frameId,
24011             name: this.frameId,
24012             frameBorder : 'no',
24013             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24014         }, this.el
24015         );
24016         
24017         
24018         this.iframe = iframe.dom;
24019
24020          this.assignDocWin();
24021         
24022         this.doc.designMode = 'on';
24023        
24024         this.doc.open();
24025         this.doc.write(this.getDocMarkup());
24026         this.doc.close();
24027
24028         
24029         var task = { // must defer to wait for browser to be ready
24030             run : function(){
24031                 //console.log("run task?" + this.doc.readyState);
24032                 this.assignDocWin();
24033                 if(this.doc.body || this.doc.readyState == 'complete'){
24034                     try {
24035                         this.doc.designMode="on";
24036                     } catch (e) {
24037                         return;
24038                     }
24039                     Roo.TaskMgr.stop(task);
24040                     this.initEditor.defer(10, this);
24041                 }
24042             },
24043             interval : 10,
24044             duration: 10000,
24045             scope: this
24046         };
24047         Roo.TaskMgr.start(task);
24048
24049     },
24050
24051     // private
24052     onResize : function(w, h)
24053     {
24054          Roo.log('resize: ' +w + ',' + h );
24055         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24056         if(!this.iframe){
24057             return;
24058         }
24059         if(typeof w == 'number'){
24060             
24061             this.iframe.style.width = w + 'px';
24062         }
24063         if(typeof h == 'number'){
24064             
24065             this.iframe.style.height = h + 'px';
24066             if(this.doc){
24067                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24068             }
24069         }
24070         
24071     },
24072
24073     /**
24074      * Toggles the editor between standard and source edit mode.
24075      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24076      */
24077     toggleSourceEdit : function(sourceEditMode){
24078         
24079         this.sourceEditMode = sourceEditMode === true;
24080         
24081         if(this.sourceEditMode){
24082  
24083             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24084             
24085         }else{
24086             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24087             //this.iframe.className = '';
24088             this.deferFocus();
24089         }
24090         //this.setSize(this.owner.wrap.getSize());
24091         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24092     },
24093
24094     
24095   
24096
24097     /**
24098      * Protected method that will not generally be called directly. If you need/want
24099      * custom HTML cleanup, this is the method you should override.
24100      * @param {String} html The HTML to be cleaned
24101      * return {String} The cleaned HTML
24102      */
24103     cleanHtml : function(html){
24104         html = String(html);
24105         if(html.length > 5){
24106             if(Roo.isSafari){ // strip safari nonsense
24107                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24108             }
24109         }
24110         if(html == '&nbsp;'){
24111             html = '';
24112         }
24113         return html;
24114     },
24115
24116     /**
24117      * HTML Editor -> Textarea
24118      * Protected method that will not generally be called directly. Syncs the contents
24119      * of the editor iframe with the textarea.
24120      */
24121     syncValue : function(){
24122         if(this.initialized){
24123             var bd = (this.doc.body || this.doc.documentElement);
24124             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24125             var html = bd.innerHTML;
24126             if(Roo.isSafari){
24127                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24128                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24129                 if(m && m[1]){
24130                     html = '<div style="'+m[0]+'">' + html + '</div>';
24131                 }
24132             }
24133             html = this.cleanHtml(html);
24134             // fix up the special chars.. normaly like back quotes in word...
24135             // however we do not want to do this with chinese..
24136             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24137                 
24138                 var cc = match.charCodeAt();
24139
24140                 // Get the character value, handling surrogate pairs
24141                 if (match.length == 2) {
24142                     // It's a surrogate pair, calculate the Unicode code point
24143                     var high = match.charCodeAt(0) - 0xD800;
24144                     var low  = match.charCodeAt(1) - 0xDC00;
24145                     cc = (high * 0x400) + low + 0x10000;
24146                 }  else if (
24147                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24148                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24149                     (cc >= 0xf900 && cc < 0xfb00 )
24150                 ) {
24151                         return match;
24152                 }  
24153          
24154                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24155                 return "&#" + cc + ";";
24156                 
24157                 
24158             });
24159             
24160             
24161              
24162             if(this.owner.fireEvent('beforesync', this, html) !== false){
24163                 this.el.dom.value = html;
24164                 this.owner.fireEvent('sync', this, html);
24165             }
24166         }
24167     },
24168
24169     /**
24170      * Protected method that will not generally be called directly. Pushes the value of the textarea
24171      * into the iframe editor.
24172      */
24173     pushValue : function(){
24174         if(this.initialized){
24175             var v = this.el.dom.value.trim();
24176             
24177 //            if(v.length < 1){
24178 //                v = '&#160;';
24179 //            }
24180             
24181             if(this.owner.fireEvent('beforepush', this, v) !== false){
24182                 var d = (this.doc.body || this.doc.documentElement);
24183                 d.innerHTML = v;
24184                 this.cleanUpPaste();
24185                 this.el.dom.value = d.innerHTML;
24186                 this.owner.fireEvent('push', this, v);
24187             }
24188         }
24189     },
24190
24191     // private
24192     deferFocus : function(){
24193         this.focus.defer(10, this);
24194     },
24195
24196     // doc'ed in Field
24197     focus : function(){
24198         if(this.win && !this.sourceEditMode){
24199             this.win.focus();
24200         }else{
24201             this.el.focus();
24202         }
24203     },
24204     
24205     assignDocWin: function()
24206     {
24207         var iframe = this.iframe;
24208         
24209          if(Roo.isIE){
24210             this.doc = iframe.contentWindow.document;
24211             this.win = iframe.contentWindow;
24212         } else {
24213 //            if (!Roo.get(this.frameId)) {
24214 //                return;
24215 //            }
24216 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24217 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24218             
24219             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24220                 return;
24221             }
24222             
24223             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24224             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24225         }
24226     },
24227     
24228     // private
24229     initEditor : function(){
24230         //console.log("INIT EDITOR");
24231         this.assignDocWin();
24232         
24233         
24234         
24235         this.doc.designMode="on";
24236         this.doc.open();
24237         this.doc.write(this.getDocMarkup());
24238         this.doc.close();
24239         
24240         var dbody = (this.doc.body || this.doc.documentElement);
24241         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24242         // this copies styles from the containing element into thsi one..
24243         // not sure why we need all of this..
24244         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24245         
24246         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24247         //ss['background-attachment'] = 'fixed'; // w3c
24248         dbody.bgProperties = 'fixed'; // ie
24249         //Roo.DomHelper.applyStyles(dbody, ss);
24250         Roo.EventManager.on(this.doc, {
24251             //'mousedown': this.onEditorEvent,
24252             'mouseup': this.onEditorEvent,
24253             'dblclick': this.onEditorEvent,
24254             'click': this.onEditorEvent,
24255             'keyup': this.onEditorEvent,
24256             buffer:100,
24257             scope: this
24258         });
24259         if(Roo.isGecko){
24260             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24261         }
24262         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24263             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24264         }
24265         this.initialized = true;
24266
24267         this.owner.fireEvent('initialize', this);
24268         this.pushValue();
24269     },
24270
24271     // private
24272     onDestroy : function(){
24273         
24274         
24275         
24276         if(this.rendered){
24277             
24278             //for (var i =0; i < this.toolbars.length;i++) {
24279             //    // fixme - ask toolbars for heights?
24280             //    this.toolbars[i].onDestroy();
24281            // }
24282             
24283             //this.wrap.dom.innerHTML = '';
24284             //this.wrap.remove();
24285         }
24286     },
24287
24288     // private
24289     onFirstFocus : function(){
24290         
24291         this.assignDocWin();
24292         
24293         
24294         this.activated = true;
24295          
24296     
24297         if(Roo.isGecko){ // prevent silly gecko errors
24298             this.win.focus();
24299             var s = this.win.getSelection();
24300             if(!s.focusNode || s.focusNode.nodeType != 3){
24301                 var r = s.getRangeAt(0);
24302                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24303                 r.collapse(true);
24304                 this.deferFocus();
24305             }
24306             try{
24307                 this.execCmd('useCSS', true);
24308                 this.execCmd('styleWithCSS', false);
24309             }catch(e){}
24310         }
24311         this.owner.fireEvent('activate', this);
24312     },
24313
24314     // private
24315     adjustFont: function(btn){
24316         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24317         //if(Roo.isSafari){ // safari
24318         //    adjust *= 2;
24319        // }
24320         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24321         if(Roo.isSafari){ // safari
24322             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24323             v =  (v < 10) ? 10 : v;
24324             v =  (v > 48) ? 48 : v;
24325             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24326             
24327         }
24328         
24329         
24330         v = Math.max(1, v+adjust);
24331         
24332         this.execCmd('FontSize', v  );
24333     },
24334
24335     onEditorEvent : function(e)
24336     {
24337         this.owner.fireEvent('editorevent', this, e);
24338       //  this.updateToolbar();
24339         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24340     },
24341
24342     insertTag : function(tg)
24343     {
24344         // could be a bit smarter... -> wrap the current selected tRoo..
24345         if (tg.toLowerCase() == 'span' ||
24346             tg.toLowerCase() == 'code' ||
24347             tg.toLowerCase() == 'sup' ||
24348             tg.toLowerCase() == 'sub' 
24349             ) {
24350             
24351             range = this.createRange(this.getSelection());
24352             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24353             wrappingNode.appendChild(range.extractContents());
24354             range.insertNode(wrappingNode);
24355
24356             return;
24357             
24358             
24359             
24360         }
24361         this.execCmd("formatblock",   tg);
24362         
24363     },
24364     
24365     insertText : function(txt)
24366     {
24367         
24368         
24369         var range = this.createRange();
24370         range.deleteContents();
24371                //alert(Sender.getAttribute('label'));
24372                
24373         range.insertNode(this.doc.createTextNode(txt));
24374     } ,
24375     
24376      
24377
24378     /**
24379      * Executes a Midas editor command on the editor document and performs necessary focus and
24380      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24381      * @param {String} cmd The Midas command
24382      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24383      */
24384     relayCmd : function(cmd, value){
24385         this.win.focus();
24386         this.execCmd(cmd, value);
24387         this.owner.fireEvent('editorevent', this);
24388         //this.updateToolbar();
24389         this.owner.deferFocus();
24390     },
24391
24392     /**
24393      * Executes a Midas editor command directly on the editor document.
24394      * For visual commands, you should use {@link #relayCmd} instead.
24395      * <b>This should only be called after the editor is initialized.</b>
24396      * @param {String} cmd The Midas command
24397      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24398      */
24399     execCmd : function(cmd, value){
24400         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24401         this.syncValue();
24402     },
24403  
24404  
24405    
24406     /**
24407      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24408      * to insert tRoo.
24409      * @param {String} text | dom node.. 
24410      */
24411     insertAtCursor : function(text)
24412     {
24413         
24414         if(!this.activated){
24415             return;
24416         }
24417         /*
24418         if(Roo.isIE){
24419             this.win.focus();
24420             var r = this.doc.selection.createRange();
24421             if(r){
24422                 r.collapse(true);
24423                 r.pasteHTML(text);
24424                 this.syncValue();
24425                 this.deferFocus();
24426             
24427             }
24428             return;
24429         }
24430         */
24431         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24432             this.win.focus();
24433             
24434             
24435             // from jquery ui (MIT licenced)
24436             var range, node;
24437             var win = this.win;
24438             
24439             if (win.getSelection && win.getSelection().getRangeAt) {
24440                 range = win.getSelection().getRangeAt(0);
24441                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24442                 range.insertNode(node);
24443             } else if (win.document.selection && win.document.selection.createRange) {
24444                 // no firefox support
24445                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24446                 win.document.selection.createRange().pasteHTML(txt);
24447             } else {
24448                 // no firefox support
24449                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24450                 this.execCmd('InsertHTML', txt);
24451             } 
24452             
24453             this.syncValue();
24454             
24455             this.deferFocus();
24456         }
24457     },
24458  // private
24459     mozKeyPress : function(e){
24460         if(e.ctrlKey){
24461             var c = e.getCharCode(), cmd;
24462           
24463             if(c > 0){
24464                 c = String.fromCharCode(c).toLowerCase();
24465                 switch(c){
24466                     case 'b':
24467                         cmd = 'bold';
24468                         break;
24469                     case 'i':
24470                         cmd = 'italic';
24471                         break;
24472                     
24473                     case 'u':
24474                         cmd = 'underline';
24475                         break;
24476                     
24477                     case 'v':
24478                         this.cleanUpPaste.defer(100, this);
24479                         return;
24480                         
24481                 }
24482                 if(cmd){
24483                     this.win.focus();
24484                     this.execCmd(cmd);
24485                     this.deferFocus();
24486                     e.preventDefault();
24487                 }
24488                 
24489             }
24490         }
24491     },
24492
24493     // private
24494     fixKeys : function(){ // load time branching for fastest keydown performance
24495         if(Roo.isIE){
24496             return function(e){
24497                 var k = e.getKey(), r;
24498                 if(k == e.TAB){
24499                     e.stopEvent();
24500                     r = this.doc.selection.createRange();
24501                     if(r){
24502                         r.collapse(true);
24503                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24504                         this.deferFocus();
24505                     }
24506                     return;
24507                 }
24508                 
24509                 if(k == e.ENTER){
24510                     r = this.doc.selection.createRange();
24511                     if(r){
24512                         var target = r.parentElement();
24513                         if(!target || target.tagName.toLowerCase() != 'li'){
24514                             e.stopEvent();
24515                             r.pasteHTML('<br />');
24516                             r.collapse(false);
24517                             r.select();
24518                         }
24519                     }
24520                 }
24521                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24522                     this.cleanUpPaste.defer(100, this);
24523                     return;
24524                 }
24525                 
24526                 
24527             };
24528         }else if(Roo.isOpera){
24529             return function(e){
24530                 var k = e.getKey();
24531                 if(k == e.TAB){
24532                     e.stopEvent();
24533                     this.win.focus();
24534                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24535                     this.deferFocus();
24536                 }
24537                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24538                     this.cleanUpPaste.defer(100, this);
24539                     return;
24540                 }
24541                 
24542             };
24543         }else if(Roo.isSafari){
24544             return function(e){
24545                 var k = e.getKey();
24546                 
24547                 if(k == e.TAB){
24548                     e.stopEvent();
24549                     this.execCmd('InsertText','\t');
24550                     this.deferFocus();
24551                     return;
24552                 }
24553                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24554                     this.cleanUpPaste.defer(100, this);
24555                     return;
24556                 }
24557                 
24558              };
24559         }
24560     }(),
24561     
24562     getAllAncestors: function()
24563     {
24564         var p = this.getSelectedNode();
24565         var a = [];
24566         if (!p) {
24567             a.push(p); // push blank onto stack..
24568             p = this.getParentElement();
24569         }
24570         
24571         
24572         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24573             a.push(p);
24574             p = p.parentNode;
24575         }
24576         a.push(this.doc.body);
24577         return a;
24578     },
24579     lastSel : false,
24580     lastSelNode : false,
24581     
24582     
24583     getSelection : function() 
24584     {
24585         this.assignDocWin();
24586         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24587     },
24588     
24589     getSelectedNode: function() 
24590     {
24591         // this may only work on Gecko!!!
24592         
24593         // should we cache this!!!!
24594         
24595         
24596         
24597          
24598         var range = this.createRange(this.getSelection()).cloneRange();
24599         
24600         if (Roo.isIE) {
24601             var parent = range.parentElement();
24602             while (true) {
24603                 var testRange = range.duplicate();
24604                 testRange.moveToElementText(parent);
24605                 if (testRange.inRange(range)) {
24606                     break;
24607                 }
24608                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24609                     break;
24610                 }
24611                 parent = parent.parentElement;
24612             }
24613             return parent;
24614         }
24615         
24616         // is ancestor a text element.
24617         var ac =  range.commonAncestorContainer;
24618         if (ac.nodeType == 3) {
24619             ac = ac.parentNode;
24620         }
24621         
24622         var ar = ac.childNodes;
24623          
24624         var nodes = [];
24625         var other_nodes = [];
24626         var has_other_nodes = false;
24627         for (var i=0;i<ar.length;i++) {
24628             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24629                 continue;
24630             }
24631             // fullly contained node.
24632             
24633             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24634                 nodes.push(ar[i]);
24635                 continue;
24636             }
24637             
24638             // probably selected..
24639             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24640                 other_nodes.push(ar[i]);
24641                 continue;
24642             }
24643             // outer..
24644             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24645                 continue;
24646             }
24647             
24648             
24649             has_other_nodes = true;
24650         }
24651         if (!nodes.length && other_nodes.length) {
24652             nodes= other_nodes;
24653         }
24654         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24655             return false;
24656         }
24657         
24658         return nodes[0];
24659     },
24660     createRange: function(sel)
24661     {
24662         // this has strange effects when using with 
24663         // top toolbar - not sure if it's a great idea.
24664         //this.editor.contentWindow.focus();
24665         if (typeof sel != "undefined") {
24666             try {
24667                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24668             } catch(e) {
24669                 return this.doc.createRange();
24670             }
24671         } else {
24672             return this.doc.createRange();
24673         }
24674     },
24675     getParentElement: function()
24676     {
24677         
24678         this.assignDocWin();
24679         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24680         
24681         var range = this.createRange(sel);
24682          
24683         try {
24684             var p = range.commonAncestorContainer;
24685             while (p.nodeType == 3) { // text node
24686                 p = p.parentNode;
24687             }
24688             return p;
24689         } catch (e) {
24690             return null;
24691         }
24692     
24693     },
24694     /***
24695      *
24696      * Range intersection.. the hard stuff...
24697      *  '-1' = before
24698      *  '0' = hits..
24699      *  '1' = after.
24700      *         [ -- selected range --- ]
24701      *   [fail]                        [fail]
24702      *
24703      *    basically..
24704      *      if end is before start or  hits it. fail.
24705      *      if start is after end or hits it fail.
24706      *
24707      *   if either hits (but other is outside. - then it's not 
24708      *   
24709      *    
24710      **/
24711     
24712     
24713     // @see http://www.thismuchiknow.co.uk/?p=64.
24714     rangeIntersectsNode : function(range, node)
24715     {
24716         var nodeRange = node.ownerDocument.createRange();
24717         try {
24718             nodeRange.selectNode(node);
24719         } catch (e) {
24720             nodeRange.selectNodeContents(node);
24721         }
24722     
24723         var rangeStartRange = range.cloneRange();
24724         rangeStartRange.collapse(true);
24725     
24726         var rangeEndRange = range.cloneRange();
24727         rangeEndRange.collapse(false);
24728     
24729         var nodeStartRange = nodeRange.cloneRange();
24730         nodeStartRange.collapse(true);
24731     
24732         var nodeEndRange = nodeRange.cloneRange();
24733         nodeEndRange.collapse(false);
24734     
24735         return rangeStartRange.compareBoundaryPoints(
24736                  Range.START_TO_START, nodeEndRange) == -1 &&
24737                rangeEndRange.compareBoundaryPoints(
24738                  Range.START_TO_START, nodeStartRange) == 1;
24739         
24740          
24741     },
24742     rangeCompareNode : function(range, node)
24743     {
24744         var nodeRange = node.ownerDocument.createRange();
24745         try {
24746             nodeRange.selectNode(node);
24747         } catch (e) {
24748             nodeRange.selectNodeContents(node);
24749         }
24750         
24751         
24752         range.collapse(true);
24753     
24754         nodeRange.collapse(true);
24755      
24756         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24757         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
24758          
24759         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24760         
24761         var nodeIsBefore   =  ss == 1;
24762         var nodeIsAfter    = ee == -1;
24763         
24764         if (nodeIsBefore && nodeIsAfter) {
24765             return 0; // outer
24766         }
24767         if (!nodeIsBefore && nodeIsAfter) {
24768             return 1; //right trailed.
24769         }
24770         
24771         if (nodeIsBefore && !nodeIsAfter) {
24772             return 2;  // left trailed.
24773         }
24774         // fully contined.
24775         return 3;
24776     },
24777
24778     // private? - in a new class?
24779     cleanUpPaste :  function()
24780     {
24781         // cleans up the whole document..
24782         Roo.log('cleanuppaste');
24783         
24784         this.cleanUpChildren(this.doc.body);
24785         var clean = this.cleanWordChars(this.doc.body.innerHTML);
24786         if (clean != this.doc.body.innerHTML) {
24787             this.doc.body.innerHTML = clean;
24788         }
24789         
24790     },
24791     
24792     cleanWordChars : function(input) {// change the chars to hex code
24793         var he = Roo.HtmlEditorCore;
24794         
24795         var output = input;
24796         Roo.each(he.swapCodes, function(sw) { 
24797             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
24798             
24799             output = output.replace(swapper, sw[1]);
24800         });
24801         
24802         return output;
24803     },
24804     
24805     
24806     cleanUpChildren : function (n)
24807     {
24808         if (!n.childNodes.length) {
24809             return;
24810         }
24811         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24812            this.cleanUpChild(n.childNodes[i]);
24813         }
24814     },
24815     
24816     
24817         
24818     
24819     cleanUpChild : function (node)
24820     {
24821         var ed = this;
24822         //console.log(node);
24823         if (node.nodeName == "#text") {
24824             // clean up silly Windows -- stuff?
24825             return; 
24826         }
24827         if (node.nodeName == "#comment") {
24828             node.parentNode.removeChild(node);
24829             // clean up silly Windows -- stuff?
24830             return; 
24831         }
24832         var lcname = node.tagName.toLowerCase();
24833         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
24834         // whitelist of tags..
24835         
24836         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
24837             // remove node.
24838             node.parentNode.removeChild(node);
24839             return;
24840             
24841         }
24842         
24843         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
24844         
24845         // spans with no attributes - just remove them..
24846         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
24847             remove_keep_children = true;
24848         }
24849         
24850         // remove <a name=....> as rendering on yahoo mailer is borked with this.
24851         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
24852         
24853         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
24854         //    remove_keep_children = true;
24855         //}
24856         
24857         if (remove_keep_children) {
24858             this.cleanUpChildren(node);
24859             // inserts everything just before this node...
24860             while (node.childNodes.length) {
24861                 var cn = node.childNodes[0];
24862                 node.removeChild(cn);
24863                 node.parentNode.insertBefore(cn, node);
24864             }
24865             node.parentNode.removeChild(node);
24866             return;
24867         }
24868         
24869         if (!node.attributes || !node.attributes.length) {
24870             
24871           
24872             
24873             
24874             this.cleanUpChildren(node);
24875             return;
24876         }
24877         
24878         function cleanAttr(n,v)
24879         {
24880             
24881             if (v.match(/^\./) || v.match(/^\//)) {
24882                 return;
24883             }
24884             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
24885                 return;
24886             }
24887             if (v.match(/^#/)) {
24888                 return;
24889             }
24890             if (v.match(/^\{/)) { // allow template editing.
24891                 return;
24892             }
24893 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
24894             node.removeAttribute(n);
24895             
24896         }
24897         
24898         var cwhite = this.cwhite;
24899         var cblack = this.cblack;
24900             
24901         function cleanStyle(n,v)
24902         {
24903             if (v.match(/expression/)) { //XSS?? should we even bother..
24904                 node.removeAttribute(n);
24905                 return;
24906             }
24907             
24908             var parts = v.split(/;/);
24909             var clean = [];
24910             
24911             Roo.each(parts, function(p) {
24912                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
24913                 if (!p.length) {
24914                     return true;
24915                 }
24916                 var l = p.split(':').shift().replace(/\s+/g,'');
24917                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
24918                 
24919                 if ( cwhite.length && cblack.indexOf(l) > -1) {
24920 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24921                     //node.removeAttribute(n);
24922                     return true;
24923                 }
24924                 //Roo.log()
24925                 // only allow 'c whitelisted system attributes'
24926                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
24927 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24928                     //node.removeAttribute(n);
24929                     return true;
24930                 }
24931                 
24932                 
24933                  
24934                 
24935                 clean.push(p);
24936                 return true;
24937             });
24938             if (clean.length) { 
24939                 node.setAttribute(n, clean.join(';'));
24940             } else {
24941                 node.removeAttribute(n);
24942             }
24943             
24944         }
24945         
24946         
24947         for (var i = node.attributes.length-1; i > -1 ; i--) {
24948             var a = node.attributes[i];
24949             //console.log(a);
24950             
24951             if (a.name.toLowerCase().substr(0,2)=='on')  {
24952                 node.removeAttribute(a.name);
24953                 continue;
24954             }
24955             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
24956                 node.removeAttribute(a.name);
24957                 continue;
24958             }
24959             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
24960                 cleanAttr(a.name,a.value); // fixme..
24961                 continue;
24962             }
24963             if (a.name == 'style') {
24964                 cleanStyle(a.name,a.value);
24965                 continue;
24966             }
24967             /// clean up MS crap..
24968             // tecnically this should be a list of valid class'es..
24969             
24970             
24971             if (a.name == 'class') {
24972                 if (a.value.match(/^Mso/)) {
24973                     node.removeAttribute('class');
24974                 }
24975                 
24976                 if (a.value.match(/^body$/)) {
24977                     node.removeAttribute('class');
24978                 }
24979                 continue;
24980             }
24981             
24982             // style cleanup!?
24983             // class cleanup?
24984             
24985         }
24986         
24987         
24988         this.cleanUpChildren(node);
24989         
24990         
24991     },
24992     
24993     /**
24994      * Clean up MS wordisms...
24995      */
24996     cleanWord : function(node)
24997     {
24998         if (!node) {
24999             this.cleanWord(this.doc.body);
25000             return;
25001         }
25002         
25003         if(
25004                 node.nodeName == 'SPAN' &&
25005                 !node.hasAttributes() &&
25006                 node.childNodes.length == 1 &&
25007                 node.firstChild.nodeName == "#text"  
25008         ) {
25009             var textNode = node.firstChild;
25010             node.removeChild(textNode);
25011             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25012                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25013             }
25014             node.parentNode.insertBefore(textNode, node);
25015             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25016                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25017             }
25018             node.parentNode.removeChild(node);
25019         }
25020         
25021         if (node.nodeName == "#text") {
25022             // clean up silly Windows -- stuff?
25023             return; 
25024         }
25025         if (node.nodeName == "#comment") {
25026             node.parentNode.removeChild(node);
25027             // clean up silly Windows -- stuff?
25028             return; 
25029         }
25030         
25031         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25032             node.parentNode.removeChild(node);
25033             return;
25034         }
25035         //Roo.log(node.tagName);
25036         // remove - but keep children..
25037         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25038             //Roo.log('-- removed');
25039             while (node.childNodes.length) {
25040                 var cn = node.childNodes[0];
25041                 node.removeChild(cn);
25042                 node.parentNode.insertBefore(cn, node);
25043                 // move node to parent - and clean it..
25044                 this.cleanWord(cn);
25045             }
25046             node.parentNode.removeChild(node);
25047             /// no need to iterate chidlren = it's got none..
25048             //this.iterateChildren(node, this.cleanWord);
25049             return;
25050         }
25051         // clean styles
25052         if (node.className.length) {
25053             
25054             var cn = node.className.split(/\W+/);
25055             var cna = [];
25056             Roo.each(cn, function(cls) {
25057                 if (cls.match(/Mso[a-zA-Z]+/)) {
25058                     return;
25059                 }
25060                 cna.push(cls);
25061             });
25062             node.className = cna.length ? cna.join(' ') : '';
25063             if (!cna.length) {
25064                 node.removeAttribute("class");
25065             }
25066         }
25067         
25068         if (node.hasAttribute("lang")) {
25069             node.removeAttribute("lang");
25070         }
25071         
25072         if (node.hasAttribute("style")) {
25073             
25074             var styles = node.getAttribute("style").split(";");
25075             var nstyle = [];
25076             Roo.each(styles, function(s) {
25077                 if (!s.match(/:/)) {
25078                     return;
25079                 }
25080                 var kv = s.split(":");
25081                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25082                     return;
25083                 }
25084                 // what ever is left... we allow.
25085                 nstyle.push(s);
25086             });
25087             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25088             if (!nstyle.length) {
25089                 node.removeAttribute('style');
25090             }
25091         }
25092         this.iterateChildren(node, this.cleanWord);
25093         
25094         
25095         
25096     },
25097     /**
25098      * iterateChildren of a Node, calling fn each time, using this as the scole..
25099      * @param {DomNode} node node to iterate children of.
25100      * @param {Function} fn method of this class to call on each item.
25101      */
25102     iterateChildren : function(node, fn)
25103     {
25104         if (!node.childNodes.length) {
25105                 return;
25106         }
25107         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25108            fn.call(this, node.childNodes[i])
25109         }
25110     },
25111     
25112     
25113     /**
25114      * cleanTableWidths.
25115      *
25116      * Quite often pasting from word etc.. results in tables with column and widths.
25117      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25118      *
25119      */
25120     cleanTableWidths : function(node)
25121     {
25122          
25123          
25124         if (!node) {
25125             this.cleanTableWidths(this.doc.body);
25126             return;
25127         }
25128         
25129         // ignore list...
25130         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25131             return; 
25132         }
25133         Roo.log(node.tagName);
25134         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25135             this.iterateChildren(node, this.cleanTableWidths);
25136             return;
25137         }
25138         if (node.hasAttribute('width')) {
25139             node.removeAttribute('width');
25140         }
25141         
25142          
25143         if (node.hasAttribute("style")) {
25144             // pretty basic...
25145             
25146             var styles = node.getAttribute("style").split(";");
25147             var nstyle = [];
25148             Roo.each(styles, function(s) {
25149                 if (!s.match(/:/)) {
25150                     return;
25151                 }
25152                 var kv = s.split(":");
25153                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25154                     return;
25155                 }
25156                 // what ever is left... we allow.
25157                 nstyle.push(s);
25158             });
25159             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25160             if (!nstyle.length) {
25161                 node.removeAttribute('style');
25162             }
25163         }
25164         
25165         this.iterateChildren(node, this.cleanTableWidths);
25166         
25167         
25168     },
25169     
25170     
25171     
25172     
25173     domToHTML : function(currentElement, depth, nopadtext) {
25174         
25175         depth = depth || 0;
25176         nopadtext = nopadtext || false;
25177     
25178         if (!currentElement) {
25179             return this.domToHTML(this.doc.body);
25180         }
25181         
25182         //Roo.log(currentElement);
25183         var j;
25184         var allText = false;
25185         var nodeName = currentElement.nodeName;
25186         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25187         
25188         if  (nodeName == '#text') {
25189             
25190             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25191         }
25192         
25193         
25194         var ret = '';
25195         if (nodeName != 'BODY') {
25196              
25197             var i = 0;
25198             // Prints the node tagName, such as <A>, <IMG>, etc
25199             if (tagName) {
25200                 var attr = [];
25201                 for(i = 0; i < currentElement.attributes.length;i++) {
25202                     // quoting?
25203                     var aname = currentElement.attributes.item(i).name;
25204                     if (!currentElement.attributes.item(i).value.length) {
25205                         continue;
25206                     }
25207                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25208                 }
25209                 
25210                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25211             } 
25212             else {
25213                 
25214                 // eack
25215             }
25216         } else {
25217             tagName = false;
25218         }
25219         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25220             return ret;
25221         }
25222         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25223             nopadtext = true;
25224         }
25225         
25226         
25227         // Traverse the tree
25228         i = 0;
25229         var currentElementChild = currentElement.childNodes.item(i);
25230         var allText = true;
25231         var innerHTML  = '';
25232         lastnode = '';
25233         while (currentElementChild) {
25234             // Formatting code (indent the tree so it looks nice on the screen)
25235             var nopad = nopadtext;
25236             if (lastnode == 'SPAN') {
25237                 nopad  = true;
25238             }
25239             // text
25240             if  (currentElementChild.nodeName == '#text') {
25241                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25242                 toadd = nopadtext ? toadd : toadd.trim();
25243                 if (!nopad && toadd.length > 80) {
25244                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25245                 }
25246                 innerHTML  += toadd;
25247                 
25248                 i++;
25249                 currentElementChild = currentElement.childNodes.item(i);
25250                 lastNode = '';
25251                 continue;
25252             }
25253             allText = false;
25254             
25255             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25256                 
25257             // Recursively traverse the tree structure of the child node
25258             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25259             lastnode = currentElementChild.nodeName;
25260             i++;
25261             currentElementChild=currentElement.childNodes.item(i);
25262         }
25263         
25264         ret += innerHTML;
25265         
25266         if (!allText) {
25267                 // The remaining code is mostly for formatting the tree
25268             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25269         }
25270         
25271         
25272         if (tagName) {
25273             ret+= "</"+tagName+">";
25274         }
25275         return ret;
25276         
25277     },
25278         
25279     applyBlacklists : function()
25280     {
25281         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25282         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25283         
25284         this.white = [];
25285         this.black = [];
25286         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25287             if (b.indexOf(tag) > -1) {
25288                 return;
25289             }
25290             this.white.push(tag);
25291             
25292         }, this);
25293         
25294         Roo.each(w, function(tag) {
25295             if (b.indexOf(tag) > -1) {
25296                 return;
25297             }
25298             if (this.white.indexOf(tag) > -1) {
25299                 return;
25300             }
25301             this.white.push(tag);
25302             
25303         }, this);
25304         
25305         
25306         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25307             if (w.indexOf(tag) > -1) {
25308                 return;
25309             }
25310             this.black.push(tag);
25311             
25312         }, this);
25313         
25314         Roo.each(b, function(tag) {
25315             if (w.indexOf(tag) > -1) {
25316                 return;
25317             }
25318             if (this.black.indexOf(tag) > -1) {
25319                 return;
25320             }
25321             this.black.push(tag);
25322             
25323         }, this);
25324         
25325         
25326         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25327         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25328         
25329         this.cwhite = [];
25330         this.cblack = [];
25331         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25332             if (b.indexOf(tag) > -1) {
25333                 return;
25334             }
25335             this.cwhite.push(tag);
25336             
25337         }, this);
25338         
25339         Roo.each(w, function(tag) {
25340             if (b.indexOf(tag) > -1) {
25341                 return;
25342             }
25343             if (this.cwhite.indexOf(tag) > -1) {
25344                 return;
25345             }
25346             this.cwhite.push(tag);
25347             
25348         }, this);
25349         
25350         
25351         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25352             if (w.indexOf(tag) > -1) {
25353                 return;
25354             }
25355             this.cblack.push(tag);
25356             
25357         }, this);
25358         
25359         Roo.each(b, function(tag) {
25360             if (w.indexOf(tag) > -1) {
25361                 return;
25362             }
25363             if (this.cblack.indexOf(tag) > -1) {
25364                 return;
25365             }
25366             this.cblack.push(tag);
25367             
25368         }, this);
25369     },
25370     
25371     setStylesheets : function(stylesheets)
25372     {
25373         if(typeof(stylesheets) == 'string'){
25374             Roo.get(this.iframe.contentDocument.head).createChild({
25375                 tag : 'link',
25376                 rel : 'stylesheet',
25377                 type : 'text/css',
25378                 href : stylesheets
25379             });
25380             
25381             return;
25382         }
25383         var _this = this;
25384      
25385         Roo.each(stylesheets, function(s) {
25386             if(!s.length){
25387                 return;
25388             }
25389             
25390             Roo.get(_this.iframe.contentDocument.head).createChild({
25391                 tag : 'link',
25392                 rel : 'stylesheet',
25393                 type : 'text/css',
25394                 href : s
25395             });
25396         });
25397
25398         
25399     },
25400     
25401     removeStylesheets : function()
25402     {
25403         var _this = this;
25404         
25405         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25406             s.remove();
25407         });
25408     },
25409     
25410     setStyle : function(style)
25411     {
25412         Roo.get(this.iframe.contentDocument.head).createChild({
25413             tag : 'style',
25414             type : 'text/css',
25415             html : style
25416         });
25417
25418         return;
25419     }
25420     
25421     // hide stuff that is not compatible
25422     /**
25423      * @event blur
25424      * @hide
25425      */
25426     /**
25427      * @event change
25428      * @hide
25429      */
25430     /**
25431      * @event focus
25432      * @hide
25433      */
25434     /**
25435      * @event specialkey
25436      * @hide
25437      */
25438     /**
25439      * @cfg {String} fieldClass @hide
25440      */
25441     /**
25442      * @cfg {String} focusClass @hide
25443      */
25444     /**
25445      * @cfg {String} autoCreate @hide
25446      */
25447     /**
25448      * @cfg {String} inputType @hide
25449      */
25450     /**
25451      * @cfg {String} invalidClass @hide
25452      */
25453     /**
25454      * @cfg {String} invalidText @hide
25455      */
25456     /**
25457      * @cfg {String} msgFx @hide
25458      */
25459     /**
25460      * @cfg {String} validateOnBlur @hide
25461      */
25462 });
25463
25464 Roo.HtmlEditorCore.white = [
25465         'area', 'br', 'img', 'input', 'hr', 'wbr',
25466         
25467        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25468        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25469        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25470        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25471        'table',   'ul',         'xmp', 
25472        
25473        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25474       'thead',   'tr', 
25475      
25476       'dir', 'menu', 'ol', 'ul', 'dl',
25477        
25478       'embed',  'object'
25479 ];
25480
25481
25482 Roo.HtmlEditorCore.black = [
25483     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25484         'applet', // 
25485         'base',   'basefont', 'bgsound', 'blink',  'body', 
25486         'frame',  'frameset', 'head',    'html',   'ilayer', 
25487         'iframe', 'layer',  'link',     'meta',    'object',   
25488         'script', 'style' ,'title',  'xml' // clean later..
25489 ];
25490 Roo.HtmlEditorCore.clean = [
25491     'script', 'style', 'title', 'xml'
25492 ];
25493 Roo.HtmlEditorCore.remove = [
25494     'font'
25495 ];
25496 // attributes..
25497
25498 Roo.HtmlEditorCore.ablack = [
25499     'on'
25500 ];
25501     
25502 Roo.HtmlEditorCore.aclean = [ 
25503     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25504 ];
25505
25506 // protocols..
25507 Roo.HtmlEditorCore.pwhite= [
25508         'http',  'https',  'mailto'
25509 ];
25510
25511 // white listed style attributes.
25512 Roo.HtmlEditorCore.cwhite= [
25513       //  'text-align', /// default is to allow most things..
25514       
25515          
25516 //        'font-size'//??
25517 ];
25518
25519 // black listed style attributes.
25520 Roo.HtmlEditorCore.cblack= [
25521       //  'font-size' -- this can be set by the project 
25522 ];
25523
25524
25525 Roo.HtmlEditorCore.swapCodes   =[ 
25526     [    8211, "--" ], 
25527     [    8212, "--" ], 
25528     [    8216,  "'" ],  
25529     [    8217, "'" ],  
25530     [    8220, '"' ],  
25531     [    8221, '"' ],  
25532     [    8226, "*" ],  
25533     [    8230, "..." ]
25534 ]; 
25535
25536     /*
25537  * - LGPL
25538  *
25539  * HtmlEditor
25540  * 
25541  */
25542
25543 /**
25544  * @class Roo.bootstrap.HtmlEditor
25545  * @extends Roo.bootstrap.TextArea
25546  * Bootstrap HtmlEditor class
25547
25548  * @constructor
25549  * Create a new HtmlEditor
25550  * @param {Object} config The config object
25551  */
25552
25553 Roo.bootstrap.HtmlEditor = function(config){
25554     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25555     if (!this.toolbars) {
25556         this.toolbars = [];
25557     }
25558     
25559     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25560     this.addEvents({
25561             /**
25562              * @event initialize
25563              * Fires when the editor is fully initialized (including the iframe)
25564              * @param {HtmlEditor} this
25565              */
25566             initialize: true,
25567             /**
25568              * @event activate
25569              * Fires when the editor is first receives the focus. Any insertion must wait
25570              * until after this event.
25571              * @param {HtmlEditor} this
25572              */
25573             activate: true,
25574              /**
25575              * @event beforesync
25576              * Fires before the textarea is updated with content from the editor iframe. Return false
25577              * to cancel the sync.
25578              * @param {HtmlEditor} this
25579              * @param {String} html
25580              */
25581             beforesync: true,
25582              /**
25583              * @event beforepush
25584              * Fires before the iframe editor is updated with content from the textarea. Return false
25585              * to cancel the push.
25586              * @param {HtmlEditor} this
25587              * @param {String} html
25588              */
25589             beforepush: true,
25590              /**
25591              * @event sync
25592              * Fires when the textarea is updated with content from the editor iframe.
25593              * @param {HtmlEditor} this
25594              * @param {String} html
25595              */
25596             sync: true,
25597              /**
25598              * @event push
25599              * Fires when the iframe editor is updated with content from the textarea.
25600              * @param {HtmlEditor} this
25601              * @param {String} html
25602              */
25603             push: true,
25604              /**
25605              * @event editmodechange
25606              * Fires when the editor switches edit modes
25607              * @param {HtmlEditor} this
25608              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25609              */
25610             editmodechange: true,
25611             /**
25612              * @event editorevent
25613              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25614              * @param {HtmlEditor} this
25615              */
25616             editorevent: true,
25617             /**
25618              * @event firstfocus
25619              * Fires when on first focus - needed by toolbars..
25620              * @param {HtmlEditor} this
25621              */
25622             firstfocus: true,
25623             /**
25624              * @event autosave
25625              * Auto save the htmlEditor value as a file into Events
25626              * @param {HtmlEditor} this
25627              */
25628             autosave: true,
25629             /**
25630              * @event savedpreview
25631              * preview the saved version of htmlEditor
25632              * @param {HtmlEditor} this
25633              */
25634             savedpreview: true
25635         });
25636 };
25637
25638
25639 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25640     
25641     
25642       /**
25643      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25644      */
25645     toolbars : false,
25646     
25647      /**
25648     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25649     */
25650     btns : [],
25651    
25652      /**
25653      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25654      *                        Roo.resizable.
25655      */
25656     resizable : false,
25657      /**
25658      * @cfg {Number} height (in pixels)
25659      */   
25660     height: 300,
25661    /**
25662      * @cfg {Number} width (in pixels)
25663      */   
25664     width: false,
25665     
25666     /**
25667      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25668      * 
25669      */
25670     stylesheets: false,
25671     
25672     // id of frame..
25673     frameId: false,
25674     
25675     // private properties
25676     validationEvent : false,
25677     deferHeight: true,
25678     initialized : false,
25679     activated : false,
25680     
25681     onFocus : Roo.emptyFn,
25682     iframePad:3,
25683     hideMode:'offsets',
25684     
25685     tbContainer : false,
25686     
25687     bodyCls : '',
25688     
25689     toolbarContainer :function() {
25690         return this.wrap.select('.x-html-editor-tb',true).first();
25691     },
25692
25693     /**
25694      * Protected method that will not generally be called directly. It
25695      * is called when the editor creates its toolbar. Override this method if you need to
25696      * add custom toolbar buttons.
25697      * @param {HtmlEditor} editor
25698      */
25699     createToolbar : function(){
25700         Roo.log('renewing');
25701         Roo.log("create toolbars");
25702         
25703         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25704         this.toolbars[0].render(this.toolbarContainer());
25705         
25706         return;
25707         
25708 //        if (!editor.toolbars || !editor.toolbars.length) {
25709 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25710 //        }
25711 //        
25712 //        for (var i =0 ; i < editor.toolbars.length;i++) {
25713 //            editor.toolbars[i] = Roo.factory(
25714 //                    typeof(editor.toolbars[i]) == 'string' ?
25715 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
25716 //                Roo.bootstrap.HtmlEditor);
25717 //            editor.toolbars[i].init(editor);
25718 //        }
25719     },
25720
25721      
25722     // private
25723     onRender : function(ct, position)
25724     {
25725        // Roo.log("Call onRender: " + this.xtype);
25726         var _t = this;
25727         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25728       
25729         this.wrap = this.inputEl().wrap({
25730             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25731         });
25732         
25733         this.editorcore.onRender(ct, position);
25734          
25735         if (this.resizable) {
25736             this.resizeEl = new Roo.Resizable(this.wrap, {
25737                 pinned : true,
25738                 wrap: true,
25739                 dynamic : true,
25740                 minHeight : this.height,
25741                 height: this.height,
25742                 handles : this.resizable,
25743                 width: this.width,
25744                 listeners : {
25745                     resize : function(r, w, h) {
25746                         _t.onResize(w,h); // -something
25747                     }
25748                 }
25749             });
25750             
25751         }
25752         this.createToolbar(this);
25753        
25754         
25755         if(!this.width && this.resizable){
25756             this.setSize(this.wrap.getSize());
25757         }
25758         if (this.resizeEl) {
25759             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25760             // should trigger onReize..
25761         }
25762         
25763     },
25764
25765     // private
25766     onResize : function(w, h)
25767     {
25768         Roo.log('resize: ' +w + ',' + h );
25769         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25770         var ew = false;
25771         var eh = false;
25772         
25773         if(this.inputEl() ){
25774             if(typeof w == 'number'){
25775                 var aw = w - this.wrap.getFrameWidth('lr');
25776                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25777                 ew = aw;
25778             }
25779             if(typeof h == 'number'){
25780                  var tbh = -11;  // fixme it needs to tool bar size!
25781                 for (var i =0; i < this.toolbars.length;i++) {
25782                     // fixme - ask toolbars for heights?
25783                     tbh += this.toolbars[i].el.getHeight();
25784                     //if (this.toolbars[i].footer) {
25785                     //    tbh += this.toolbars[i].footer.el.getHeight();
25786                     //}
25787                 }
25788               
25789                 
25790                 
25791                 
25792                 
25793                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25794                 ah -= 5; // knock a few pixes off for look..
25795                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
25796                 var eh = ah;
25797             }
25798         }
25799         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
25800         this.editorcore.onResize(ew,eh);
25801         
25802     },
25803
25804     /**
25805      * Toggles the editor between standard and source edit mode.
25806      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25807      */
25808     toggleSourceEdit : function(sourceEditMode)
25809     {
25810         this.editorcore.toggleSourceEdit(sourceEditMode);
25811         
25812         if(this.editorcore.sourceEditMode){
25813             Roo.log('editor - showing textarea');
25814             
25815 //            Roo.log('in');
25816 //            Roo.log(this.syncValue());
25817             this.syncValue();
25818             this.inputEl().removeClass(['hide', 'x-hidden']);
25819             this.inputEl().dom.removeAttribute('tabIndex');
25820             this.inputEl().focus();
25821         }else{
25822             Roo.log('editor - hiding textarea');
25823 //            Roo.log('out')
25824 //            Roo.log(this.pushValue()); 
25825             this.pushValue();
25826             
25827             this.inputEl().addClass(['hide', 'x-hidden']);
25828             this.inputEl().dom.setAttribute('tabIndex', -1);
25829             //this.deferFocus();
25830         }
25831          
25832         if(this.resizable){
25833             this.setSize(this.wrap.getSize());
25834         }
25835         
25836         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
25837     },
25838  
25839     // private (for BoxComponent)
25840     adjustSize : Roo.BoxComponent.prototype.adjustSize,
25841
25842     // private (for BoxComponent)
25843     getResizeEl : function(){
25844         return this.wrap;
25845     },
25846
25847     // private (for BoxComponent)
25848     getPositionEl : function(){
25849         return this.wrap;
25850     },
25851
25852     // private
25853     initEvents : function(){
25854         this.originalValue = this.getValue();
25855     },
25856
25857 //    /**
25858 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25859 //     * @method
25860 //     */
25861 //    markInvalid : Roo.emptyFn,
25862 //    /**
25863 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25864 //     * @method
25865 //     */
25866 //    clearInvalid : Roo.emptyFn,
25867
25868     setValue : function(v){
25869         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
25870         this.editorcore.pushValue();
25871     },
25872
25873      
25874     // private
25875     deferFocus : function(){
25876         this.focus.defer(10, this);
25877     },
25878
25879     // doc'ed in Field
25880     focus : function(){
25881         this.editorcore.focus();
25882         
25883     },
25884       
25885
25886     // private
25887     onDestroy : function(){
25888         
25889         
25890         
25891         if(this.rendered){
25892             
25893             for (var i =0; i < this.toolbars.length;i++) {
25894                 // fixme - ask toolbars for heights?
25895                 this.toolbars[i].onDestroy();
25896             }
25897             
25898             this.wrap.dom.innerHTML = '';
25899             this.wrap.remove();
25900         }
25901     },
25902
25903     // private
25904     onFirstFocus : function(){
25905         //Roo.log("onFirstFocus");
25906         this.editorcore.onFirstFocus();
25907          for (var i =0; i < this.toolbars.length;i++) {
25908             this.toolbars[i].onFirstFocus();
25909         }
25910         
25911     },
25912     
25913     // private
25914     syncValue : function()
25915     {   
25916         this.editorcore.syncValue();
25917     },
25918     
25919     pushValue : function()
25920     {   
25921         this.editorcore.pushValue();
25922     }
25923      
25924     
25925     // hide stuff that is not compatible
25926     /**
25927      * @event blur
25928      * @hide
25929      */
25930     /**
25931      * @event change
25932      * @hide
25933      */
25934     /**
25935      * @event focus
25936      * @hide
25937      */
25938     /**
25939      * @event specialkey
25940      * @hide
25941      */
25942     /**
25943      * @cfg {String} fieldClass @hide
25944      */
25945     /**
25946      * @cfg {String} focusClass @hide
25947      */
25948     /**
25949      * @cfg {String} autoCreate @hide
25950      */
25951     /**
25952      * @cfg {String} inputType @hide
25953      */
25954      
25955     /**
25956      * @cfg {String} invalidText @hide
25957      */
25958     /**
25959      * @cfg {String} msgFx @hide
25960      */
25961     /**
25962      * @cfg {String} validateOnBlur @hide
25963      */
25964 });
25965  
25966     
25967    
25968    
25969    
25970       
25971 Roo.namespace('Roo.bootstrap.htmleditor');
25972 /**
25973  * @class Roo.bootstrap.HtmlEditorToolbar1
25974  * Basic Toolbar
25975  * 
25976  * @example
25977  * Usage:
25978  *
25979  new Roo.bootstrap.HtmlEditor({
25980     ....
25981     toolbars : [
25982         new Roo.bootstrap.HtmlEditorToolbar1({
25983             disable : { fonts: 1 , format: 1, ..., ... , ...],
25984             btns : [ .... ]
25985         })
25986     }
25987      
25988  * 
25989  * @cfg {Object} disable List of elements to disable..
25990  * @cfg {Array} btns List of additional buttons.
25991  * 
25992  * 
25993  * NEEDS Extra CSS? 
25994  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
25995  */
25996  
25997 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
25998 {
25999     
26000     Roo.apply(this, config);
26001     
26002     // default disabled, based on 'good practice'..
26003     this.disable = this.disable || {};
26004     Roo.applyIf(this.disable, {
26005         fontSize : true,
26006         colors : true,
26007         specialElements : true
26008     });
26009     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26010     
26011     this.editor = config.editor;
26012     this.editorcore = config.editor.editorcore;
26013     
26014     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26015     
26016     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26017     // dont call parent... till later.
26018 }
26019 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26020      
26021     bar : true,
26022     
26023     editor : false,
26024     editorcore : false,
26025     
26026     
26027     formats : [
26028         "p" ,  
26029         "h1","h2","h3","h4","h5","h6", 
26030         "pre", "code", 
26031         "abbr", "acronym", "address", "cite", "samp", "var",
26032         'div','span'
26033     ],
26034     
26035     onRender : function(ct, position)
26036     {
26037        // Roo.log("Call onRender: " + this.xtype);
26038         
26039        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26040        Roo.log(this.el);
26041        this.el.dom.style.marginBottom = '0';
26042        var _this = this;
26043        var editorcore = this.editorcore;
26044        var editor= this.editor;
26045        
26046        var children = [];
26047        var btn = function(id,cmd , toggle, handler, html){
26048        
26049             var  event = toggle ? 'toggle' : 'click';
26050        
26051             var a = {
26052                 size : 'sm',
26053                 xtype: 'Button',
26054                 xns: Roo.bootstrap,
26055                 //glyphicon : id,
26056                 fa: id,
26057                 cmd : id || cmd,
26058                 enableToggle:toggle !== false,
26059                 html : html || '',
26060                 pressed : toggle ? false : null,
26061                 listeners : {}
26062             };
26063             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26064                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26065             };
26066             children.push(a);
26067             return a;
26068        }
26069        
26070     //    var cb_box = function...
26071         
26072         var style = {
26073                 xtype: 'Button',
26074                 size : 'sm',
26075                 xns: Roo.bootstrap,
26076                 fa : 'font',
26077                 //html : 'submit'
26078                 menu : {
26079                     xtype: 'Menu',
26080                     xns: Roo.bootstrap,
26081                     items:  []
26082                 }
26083         };
26084         Roo.each(this.formats, function(f) {
26085             style.menu.items.push({
26086                 xtype :'MenuItem',
26087                 xns: Roo.bootstrap,
26088                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26089                 tagname : f,
26090                 listeners : {
26091                     click : function()
26092                     {
26093                         editorcore.insertTag(this.tagname);
26094                         editor.focus();
26095                     }
26096                 }
26097                 
26098             });
26099         });
26100         children.push(style);   
26101         
26102         btn('bold',false,true);
26103         btn('italic',false,true);
26104         btn('align-left', 'justifyleft',true);
26105         btn('align-center', 'justifycenter',true);
26106         btn('align-right' , 'justifyright',true);
26107         btn('link', false, false, function(btn) {
26108             //Roo.log("create link?");
26109             var url = prompt(this.createLinkText, this.defaultLinkValue);
26110             if(url && url != 'http:/'+'/'){
26111                 this.editorcore.relayCmd('createlink', url);
26112             }
26113         }),
26114         btn('list','insertunorderedlist',true);
26115         btn('pencil', false,true, function(btn){
26116                 Roo.log(this);
26117                 this.toggleSourceEdit(btn.pressed);
26118         });
26119         
26120         if (this.editor.btns.length > 0) {
26121             for (var i = 0; i<this.editor.btns.length; i++) {
26122                 children.push(this.editor.btns[i]);
26123             }
26124         }
26125         
26126         /*
26127         var cog = {
26128                 xtype: 'Button',
26129                 size : 'sm',
26130                 xns: Roo.bootstrap,
26131                 glyphicon : 'cog',
26132                 //html : 'submit'
26133                 menu : {
26134                     xtype: 'Menu',
26135                     xns: Roo.bootstrap,
26136                     items:  []
26137                 }
26138         };
26139         
26140         cog.menu.items.push({
26141             xtype :'MenuItem',
26142             xns: Roo.bootstrap,
26143             html : Clean styles,
26144             tagname : f,
26145             listeners : {
26146                 click : function()
26147                 {
26148                     editorcore.insertTag(this.tagname);
26149                     editor.focus();
26150                 }
26151             }
26152             
26153         });
26154        */
26155         
26156          
26157        this.xtype = 'NavSimplebar';
26158         
26159         for(var i=0;i< children.length;i++) {
26160             
26161             this.buttons.add(this.addxtypeChild(children[i]));
26162             
26163         }
26164         
26165         editor.on('editorevent', this.updateToolbar, this);
26166     },
26167     onBtnClick : function(id)
26168     {
26169        this.editorcore.relayCmd(id);
26170        this.editorcore.focus();
26171     },
26172     
26173     /**
26174      * Protected method that will not generally be called directly. It triggers
26175      * a toolbar update by reading the markup state of the current selection in the editor.
26176      */
26177     updateToolbar: function(){
26178
26179         if(!this.editorcore.activated){
26180             this.editor.onFirstFocus(); // is this neeed?
26181             return;
26182         }
26183
26184         var btns = this.buttons; 
26185         var doc = this.editorcore.doc;
26186         btns.get('bold').setActive(doc.queryCommandState('bold'));
26187         btns.get('italic').setActive(doc.queryCommandState('italic'));
26188         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26189         
26190         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26191         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26192         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26193         
26194         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26195         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26196          /*
26197         
26198         var ans = this.editorcore.getAllAncestors();
26199         if (this.formatCombo) {
26200             
26201             
26202             var store = this.formatCombo.store;
26203             this.formatCombo.setValue("");
26204             for (var i =0; i < ans.length;i++) {
26205                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26206                     // select it..
26207                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26208                     break;
26209                 }
26210             }
26211         }
26212         
26213         
26214         
26215         // hides menus... - so this cant be on a menu...
26216         Roo.bootstrap.MenuMgr.hideAll();
26217         */
26218         Roo.bootstrap.MenuMgr.hideAll();
26219         //this.editorsyncValue();
26220     },
26221     onFirstFocus: function() {
26222         this.buttons.each(function(item){
26223            item.enable();
26224         });
26225     },
26226     toggleSourceEdit : function(sourceEditMode){
26227         
26228           
26229         if(sourceEditMode){
26230             Roo.log("disabling buttons");
26231            this.buttons.each( function(item){
26232                 if(item.cmd != 'pencil'){
26233                     item.disable();
26234                 }
26235             });
26236           
26237         }else{
26238             Roo.log("enabling buttons");
26239             if(this.editorcore.initialized){
26240                 this.buttons.each( function(item){
26241                     item.enable();
26242                 });
26243             }
26244             
26245         }
26246         Roo.log("calling toggole on editor");
26247         // tell the editor that it's been pressed..
26248         this.editor.toggleSourceEdit(sourceEditMode);
26249        
26250     }
26251 });
26252
26253
26254
26255
26256  
26257 /*
26258  * - LGPL
26259  */
26260
26261 /**
26262  * @class Roo.bootstrap.Markdown
26263  * @extends Roo.bootstrap.TextArea
26264  * Bootstrap Showdown editable area
26265  * @cfg {string} content
26266  * 
26267  * @constructor
26268  * Create a new Showdown
26269  */
26270
26271 Roo.bootstrap.Markdown = function(config){
26272     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26273    
26274 };
26275
26276 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26277     
26278     editing :false,
26279     
26280     initEvents : function()
26281     {
26282         
26283         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26284         this.markdownEl = this.el.createChild({
26285             cls : 'roo-markdown-area'
26286         });
26287         this.inputEl().addClass('d-none');
26288         if (this.getValue() == '') {
26289             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26290             
26291         } else {
26292             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26293         }
26294         this.markdownEl.on('click', this.toggleTextEdit, this);
26295         this.on('blur', this.toggleTextEdit, this);
26296         this.on('specialkey', this.resizeTextArea, this);
26297     },
26298     
26299     toggleTextEdit : function()
26300     {
26301         var sh = this.markdownEl.getHeight();
26302         this.inputEl().addClass('d-none');
26303         this.markdownEl.addClass('d-none');
26304         if (!this.editing) {
26305             // show editor?
26306             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26307             this.inputEl().removeClass('d-none');
26308             this.inputEl().focus();
26309             this.editing = true;
26310             return;
26311         }
26312         // show showdown...
26313         this.updateMarkdown();
26314         this.markdownEl.removeClass('d-none');
26315         this.editing = false;
26316         return;
26317     },
26318     updateMarkdown : function()
26319     {
26320         if (this.getValue() == '') {
26321             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26322             return;
26323         }
26324  
26325         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26326     },
26327     
26328     resizeTextArea: function () {
26329         
26330         var sh = 100;
26331         Roo.log([sh, this.getValue().split("\n").length * 30]);
26332         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26333     },
26334     setValue : function(val)
26335     {
26336         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26337         if (!this.editing) {
26338             this.updateMarkdown();
26339         }
26340         
26341     },
26342     focus : function()
26343     {
26344         if (!this.editing) {
26345             this.toggleTextEdit();
26346         }
26347         
26348     }
26349
26350
26351 });
26352 /**
26353  * @class Roo.bootstrap.Table.AbstractSelectionModel
26354  * @extends Roo.util.Observable
26355  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26356  * implemented by descendant classes.  This class should not be directly instantiated.
26357  * @constructor
26358  */
26359 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26360     this.locked = false;
26361     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26362 };
26363
26364
26365 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26366     /** @ignore Called by the grid automatically. Do not call directly. */
26367     init : function(grid){
26368         this.grid = grid;
26369         this.initEvents();
26370     },
26371
26372     /**
26373      * Locks the selections.
26374      */
26375     lock : function(){
26376         this.locked = true;
26377     },
26378
26379     /**
26380      * Unlocks the selections.
26381      */
26382     unlock : function(){
26383         this.locked = false;
26384     },
26385
26386     /**
26387      * Returns true if the selections are locked.
26388      * @return {Boolean}
26389      */
26390     isLocked : function(){
26391         return this.locked;
26392     },
26393     
26394     
26395     initEvents : function ()
26396     {
26397         
26398     }
26399 });
26400 /**
26401  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26402  * @class Roo.bootstrap.Table.RowSelectionModel
26403  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26404  * It supports multiple selections and keyboard selection/navigation. 
26405  * @constructor
26406  * @param {Object} config
26407  */
26408
26409 Roo.bootstrap.Table.RowSelectionModel = function(config){
26410     Roo.apply(this, config);
26411     this.selections = new Roo.util.MixedCollection(false, function(o){
26412         return o.id;
26413     });
26414
26415     this.last = false;
26416     this.lastActive = false;
26417
26418     this.addEvents({
26419         /**
26420              * @event selectionchange
26421              * Fires when the selection changes
26422              * @param {SelectionModel} this
26423              */
26424             "selectionchange" : true,
26425         /**
26426              * @event afterselectionchange
26427              * Fires after the selection changes (eg. by key press or clicking)
26428              * @param {SelectionModel} this
26429              */
26430             "afterselectionchange" : true,
26431         /**
26432              * @event beforerowselect
26433              * Fires when a row is selected being selected, return false to cancel.
26434              * @param {SelectionModel} this
26435              * @param {Number} rowIndex The selected index
26436              * @param {Boolean} keepExisting False if other selections will be cleared
26437              */
26438             "beforerowselect" : true,
26439         /**
26440              * @event rowselect
26441              * Fires when a row is selected.
26442              * @param {SelectionModel} this
26443              * @param {Number} rowIndex The selected index
26444              * @param {Roo.data.Record} r The record
26445              */
26446             "rowselect" : true,
26447         /**
26448              * @event rowdeselect
26449              * Fires when a row is deselected.
26450              * @param {SelectionModel} this
26451              * @param {Number} rowIndex The selected index
26452              */
26453         "rowdeselect" : true
26454     });
26455     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26456     this.locked = false;
26457  };
26458
26459 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26460     /**
26461      * @cfg {Boolean} singleSelect
26462      * True to allow selection of only one row at a time (defaults to false)
26463      */
26464     singleSelect : false,
26465
26466     // private
26467     initEvents : function()
26468     {
26469
26470         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26471         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26472         //}else{ // allow click to work like normal
26473          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26474         //}
26475         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26476         this.grid.on("rowclick", this.handleMouseDown, this);
26477         
26478         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26479             "up" : function(e){
26480                 if(!e.shiftKey){
26481                     this.selectPrevious(e.shiftKey);
26482                 }else if(this.last !== false && this.lastActive !== false){
26483                     var last = this.last;
26484                     this.selectRange(this.last,  this.lastActive-1);
26485                     this.grid.getView().focusRow(this.lastActive);
26486                     if(last !== false){
26487                         this.last = last;
26488                     }
26489                 }else{
26490                     this.selectFirstRow();
26491                 }
26492                 this.fireEvent("afterselectionchange", this);
26493             },
26494             "down" : function(e){
26495                 if(!e.shiftKey){
26496                     this.selectNext(e.shiftKey);
26497                 }else if(this.last !== false && this.lastActive !== false){
26498                     var last = this.last;
26499                     this.selectRange(this.last,  this.lastActive+1);
26500                     this.grid.getView().focusRow(this.lastActive);
26501                     if(last !== false){
26502                         this.last = last;
26503                     }
26504                 }else{
26505                     this.selectFirstRow();
26506                 }
26507                 this.fireEvent("afterselectionchange", this);
26508             },
26509             scope: this
26510         });
26511         this.grid.store.on('load', function(){
26512             this.selections.clear();
26513         },this);
26514         /*
26515         var view = this.grid.view;
26516         view.on("refresh", this.onRefresh, this);
26517         view.on("rowupdated", this.onRowUpdated, this);
26518         view.on("rowremoved", this.onRemove, this);
26519         */
26520     },
26521
26522     // private
26523     onRefresh : function()
26524     {
26525         var ds = this.grid.store, i, v = this.grid.view;
26526         var s = this.selections;
26527         s.each(function(r){
26528             if((i = ds.indexOfId(r.id)) != -1){
26529                 v.onRowSelect(i);
26530             }else{
26531                 s.remove(r);
26532             }
26533         });
26534     },
26535
26536     // private
26537     onRemove : function(v, index, r){
26538         this.selections.remove(r);
26539     },
26540
26541     // private
26542     onRowUpdated : function(v, index, r){
26543         if(this.isSelected(r)){
26544             v.onRowSelect(index);
26545         }
26546     },
26547
26548     /**
26549      * Select records.
26550      * @param {Array} records The records to select
26551      * @param {Boolean} keepExisting (optional) True to keep existing selections
26552      */
26553     selectRecords : function(records, keepExisting)
26554     {
26555         if(!keepExisting){
26556             this.clearSelections();
26557         }
26558             var ds = this.grid.store;
26559         for(var i = 0, len = records.length; i < len; i++){
26560             this.selectRow(ds.indexOf(records[i]), true);
26561         }
26562     },
26563
26564     /**
26565      * Gets the number of selected rows.
26566      * @return {Number}
26567      */
26568     getCount : function(){
26569         return this.selections.length;
26570     },
26571
26572     /**
26573      * Selects the first row in the grid.
26574      */
26575     selectFirstRow : function(){
26576         this.selectRow(0);
26577     },
26578
26579     /**
26580      * Select the last row.
26581      * @param {Boolean} keepExisting (optional) True to keep existing selections
26582      */
26583     selectLastRow : function(keepExisting){
26584         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26585         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26586     },
26587
26588     /**
26589      * Selects the row immediately following the last selected row.
26590      * @param {Boolean} keepExisting (optional) True to keep existing selections
26591      */
26592     selectNext : function(keepExisting)
26593     {
26594             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26595             this.selectRow(this.last+1, keepExisting);
26596             this.grid.getView().focusRow(this.last);
26597         }
26598     },
26599
26600     /**
26601      * Selects the row that precedes the last selected row.
26602      * @param {Boolean} keepExisting (optional) True to keep existing selections
26603      */
26604     selectPrevious : function(keepExisting){
26605         if(this.last){
26606             this.selectRow(this.last-1, keepExisting);
26607             this.grid.getView().focusRow(this.last);
26608         }
26609     },
26610
26611     /**
26612      * Returns the selected records
26613      * @return {Array} Array of selected records
26614      */
26615     getSelections : function(){
26616         return [].concat(this.selections.items);
26617     },
26618
26619     /**
26620      * Returns the first selected record.
26621      * @return {Record}
26622      */
26623     getSelected : function(){
26624         return this.selections.itemAt(0);
26625     },
26626
26627
26628     /**
26629      * Clears all selections.
26630      */
26631     clearSelections : function(fast)
26632     {
26633         if(this.locked) {
26634             return;
26635         }
26636         if(fast !== true){
26637                 var ds = this.grid.store;
26638             var s = this.selections;
26639             s.each(function(r){
26640                 this.deselectRow(ds.indexOfId(r.id));
26641             }, this);
26642             s.clear();
26643         }else{
26644             this.selections.clear();
26645         }
26646         this.last = false;
26647     },
26648
26649
26650     /**
26651      * Selects all rows.
26652      */
26653     selectAll : function(){
26654         if(this.locked) {
26655             return;
26656         }
26657         this.selections.clear();
26658         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26659             this.selectRow(i, true);
26660         }
26661     },
26662
26663     /**
26664      * Returns True if there is a selection.
26665      * @return {Boolean}
26666      */
26667     hasSelection : function(){
26668         return this.selections.length > 0;
26669     },
26670
26671     /**
26672      * Returns True if the specified row is selected.
26673      * @param {Number/Record} record The record or index of the record to check
26674      * @return {Boolean}
26675      */
26676     isSelected : function(index){
26677             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26678         return (r && this.selections.key(r.id) ? true : false);
26679     },
26680
26681     /**
26682      * Returns True if the specified record id is selected.
26683      * @param {String} id The id of record to check
26684      * @return {Boolean}
26685      */
26686     isIdSelected : function(id){
26687         return (this.selections.key(id) ? true : false);
26688     },
26689
26690
26691     // private
26692     handleMouseDBClick : function(e, t){
26693         
26694     },
26695     // private
26696     handleMouseDown : function(e, t)
26697     {
26698             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26699         if(this.isLocked() || rowIndex < 0 ){
26700             return;
26701         };
26702         if(e.shiftKey && this.last !== false){
26703             var last = this.last;
26704             this.selectRange(last, rowIndex, e.ctrlKey);
26705             this.last = last; // reset the last
26706             t.focus();
26707     
26708         }else{
26709             var isSelected = this.isSelected(rowIndex);
26710             //Roo.log("select row:" + rowIndex);
26711             if(isSelected){
26712                 this.deselectRow(rowIndex);
26713             } else {
26714                         this.selectRow(rowIndex, true);
26715             }
26716     
26717             /*
26718                 if(e.button !== 0 && isSelected){
26719                 alert('rowIndex 2: ' + rowIndex);
26720                     view.focusRow(rowIndex);
26721                 }else if(e.ctrlKey && isSelected){
26722                     this.deselectRow(rowIndex);
26723                 }else if(!isSelected){
26724                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26725                     view.focusRow(rowIndex);
26726                 }
26727             */
26728         }
26729         this.fireEvent("afterselectionchange", this);
26730     },
26731     // private
26732     handleDragableRowClick :  function(grid, rowIndex, e) 
26733     {
26734         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26735             this.selectRow(rowIndex, false);
26736             grid.view.focusRow(rowIndex);
26737              this.fireEvent("afterselectionchange", this);
26738         }
26739     },
26740     
26741     /**
26742      * Selects multiple rows.
26743      * @param {Array} rows Array of the indexes of the row to select
26744      * @param {Boolean} keepExisting (optional) True to keep existing selections
26745      */
26746     selectRows : function(rows, keepExisting){
26747         if(!keepExisting){
26748             this.clearSelections();
26749         }
26750         for(var i = 0, len = rows.length; i < len; i++){
26751             this.selectRow(rows[i], true);
26752         }
26753     },
26754
26755     /**
26756      * Selects a range of rows. All rows in between startRow and endRow are also selected.
26757      * @param {Number} startRow The index of the first row in the range
26758      * @param {Number} endRow The index of the last row in the range
26759      * @param {Boolean} keepExisting (optional) True to retain existing selections
26760      */
26761     selectRange : function(startRow, endRow, keepExisting){
26762         if(this.locked) {
26763             return;
26764         }
26765         if(!keepExisting){
26766             this.clearSelections();
26767         }
26768         if(startRow <= endRow){
26769             for(var i = startRow; i <= endRow; i++){
26770                 this.selectRow(i, true);
26771             }
26772         }else{
26773             for(var i = startRow; i >= endRow; i--){
26774                 this.selectRow(i, true);
26775             }
26776         }
26777     },
26778
26779     /**
26780      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26781      * @param {Number} startRow The index of the first row in the range
26782      * @param {Number} endRow The index of the last row in the range
26783      */
26784     deselectRange : function(startRow, endRow, preventViewNotify){
26785         if(this.locked) {
26786             return;
26787         }
26788         for(var i = startRow; i <= endRow; i++){
26789             this.deselectRow(i, preventViewNotify);
26790         }
26791     },
26792
26793     /**
26794      * Selects a row.
26795      * @param {Number} row The index of the row to select
26796      * @param {Boolean} keepExisting (optional) True to keep existing selections
26797      */
26798     selectRow : function(index, keepExisting, preventViewNotify)
26799     {
26800             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26801             return;
26802         }
26803         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26804             if(!keepExisting || this.singleSelect){
26805                 this.clearSelections();
26806             }
26807             
26808             var r = this.grid.store.getAt(index);
26809             //console.log('selectRow - record id :' + r.id);
26810             
26811             this.selections.add(r);
26812             this.last = this.lastActive = index;
26813             if(!preventViewNotify){
26814                 var proxy = new Roo.Element(
26815                                 this.grid.getRowDom(index)
26816                 );
26817                 proxy.addClass('bg-info info');
26818             }
26819             this.fireEvent("rowselect", this, index, r);
26820             this.fireEvent("selectionchange", this);
26821         }
26822     },
26823
26824     /**
26825      * Deselects a row.
26826      * @param {Number} row The index of the row to deselect
26827      */
26828     deselectRow : function(index, preventViewNotify)
26829     {
26830         if(this.locked) {
26831             return;
26832         }
26833         if(this.last == index){
26834             this.last = false;
26835         }
26836         if(this.lastActive == index){
26837             this.lastActive = false;
26838         }
26839         
26840         var r = this.grid.store.getAt(index);
26841         if (!r) {
26842             return;
26843         }
26844         
26845         this.selections.remove(r);
26846         //.console.log('deselectRow - record id :' + r.id);
26847         if(!preventViewNotify){
26848         
26849             var proxy = new Roo.Element(
26850                 this.grid.getRowDom(index)
26851             );
26852             proxy.removeClass('bg-info info');
26853         }
26854         this.fireEvent("rowdeselect", this, index);
26855         this.fireEvent("selectionchange", this);
26856     },
26857
26858     // private
26859     restoreLast : function(){
26860         if(this._last){
26861             this.last = this._last;
26862         }
26863     },
26864
26865     // private
26866     acceptsNav : function(row, col, cm){
26867         return !cm.isHidden(col) && cm.isCellEditable(col, row);
26868     },
26869
26870     // private
26871     onEditorKey : function(field, e){
26872         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
26873         if(k == e.TAB){
26874             e.stopEvent();
26875             ed.completeEdit();
26876             if(e.shiftKey){
26877                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
26878             }else{
26879                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
26880             }
26881         }else if(k == e.ENTER && !e.ctrlKey){
26882             e.stopEvent();
26883             ed.completeEdit();
26884             if(e.shiftKey){
26885                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
26886             }else{
26887                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
26888             }
26889         }else if(k == e.ESC){
26890             ed.cancelEdit();
26891         }
26892         if(newCell){
26893             g.startEditing(newCell[0], newCell[1]);
26894         }
26895     }
26896 });
26897 /*
26898  * Based on:
26899  * Ext JS Library 1.1.1
26900  * Copyright(c) 2006-2007, Ext JS, LLC.
26901  *
26902  * Originally Released Under LGPL - original licence link has changed is not relivant.
26903  *
26904  * Fork - LGPL
26905  * <script type="text/javascript">
26906  */
26907  
26908 /**
26909  * @class Roo.bootstrap.PagingToolbar
26910  * @extends Roo.bootstrap.NavSimplebar
26911  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
26912  * @constructor
26913  * Create a new PagingToolbar
26914  * @param {Object} config The config object
26915  * @param {Roo.data.Store} store
26916  */
26917 Roo.bootstrap.PagingToolbar = function(config)
26918 {
26919     // old args format still supported... - xtype is prefered..
26920         // created from xtype...
26921     
26922     this.ds = config.dataSource;
26923     
26924     if (config.store && !this.ds) {
26925         this.store= Roo.factory(config.store, Roo.data);
26926         this.ds = this.store;
26927         this.ds.xmodule = this.xmodule || false;
26928     }
26929     
26930     this.toolbarItems = [];
26931     if (config.items) {
26932         this.toolbarItems = config.items;
26933     }
26934     
26935     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
26936     
26937     this.cursor = 0;
26938     
26939     if (this.ds) { 
26940         this.bind(this.ds);
26941     }
26942     
26943     if (Roo.bootstrap.version == 4) {
26944         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
26945     } else {
26946         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
26947     }
26948     
26949 };
26950
26951 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
26952     /**
26953      * @cfg {Roo.data.Store} dataSource
26954      * The underlying data store providing the paged data
26955      */
26956     /**
26957      * @cfg {String/HTMLElement/Element} container
26958      * container The id or element that will contain the toolbar
26959      */
26960     /**
26961      * @cfg {Boolean} displayInfo
26962      * True to display the displayMsg (defaults to false)
26963      */
26964     /**
26965      * @cfg {Number} pageSize
26966      * The number of records to display per page (defaults to 20)
26967      */
26968     pageSize: 20,
26969     /**
26970      * @cfg {String} displayMsg
26971      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
26972      */
26973     displayMsg : 'Displaying {0} - {1} of {2}',
26974     /**
26975      * @cfg {String} emptyMsg
26976      * The message to display when no records are found (defaults to "No data to display")
26977      */
26978     emptyMsg : 'No data to display',
26979     /**
26980      * Customizable piece of the default paging text (defaults to "Page")
26981      * @type String
26982      */
26983     beforePageText : "Page",
26984     /**
26985      * Customizable piece of the default paging text (defaults to "of %0")
26986      * @type String
26987      */
26988     afterPageText : "of {0}",
26989     /**
26990      * Customizable piece of the default paging text (defaults to "First Page")
26991      * @type String
26992      */
26993     firstText : "First Page",
26994     /**
26995      * Customizable piece of the default paging text (defaults to "Previous Page")
26996      * @type String
26997      */
26998     prevText : "Previous Page",
26999     /**
27000      * Customizable piece of the default paging text (defaults to "Next Page")
27001      * @type String
27002      */
27003     nextText : "Next Page",
27004     /**
27005      * Customizable piece of the default paging text (defaults to "Last Page")
27006      * @type String
27007      */
27008     lastText : "Last Page",
27009     /**
27010      * Customizable piece of the default paging text (defaults to "Refresh")
27011      * @type String
27012      */
27013     refreshText : "Refresh",
27014
27015     buttons : false,
27016     // private
27017     onRender : function(ct, position) 
27018     {
27019         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27020         this.navgroup.parentId = this.id;
27021         this.navgroup.onRender(this.el, null);
27022         // add the buttons to the navgroup
27023         
27024         if(this.displayInfo){
27025             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27026             this.displayEl = this.el.select('.x-paging-info', true).first();
27027 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27028 //            this.displayEl = navel.el.select('span',true).first();
27029         }
27030         
27031         var _this = this;
27032         
27033         if(this.buttons){
27034             Roo.each(_this.buttons, function(e){ // this might need to use render????
27035                Roo.factory(e).render(_this.el);
27036             });
27037         }
27038             
27039         Roo.each(_this.toolbarItems, function(e) {
27040             _this.navgroup.addItem(e);
27041         });
27042         
27043         
27044         this.first = this.navgroup.addItem({
27045             tooltip: this.firstText,
27046             cls: "prev btn-outline-secondary",
27047             html : ' <i class="fa fa-step-backward"></i>',
27048             disabled: true,
27049             preventDefault: true,
27050             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27051         });
27052         
27053         this.prev =  this.navgroup.addItem({
27054             tooltip: this.prevText,
27055             cls: "prev btn-outline-secondary",
27056             html : ' <i class="fa fa-backward"></i>',
27057             disabled: true,
27058             preventDefault: true,
27059             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27060         });
27061     //this.addSeparator();
27062         
27063         
27064         var field = this.navgroup.addItem( {
27065             tagtype : 'span',
27066             cls : 'x-paging-position  btn-outline-secondary',
27067              disabled: true,
27068             html : this.beforePageText  +
27069                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27070                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27071          } ); //?? escaped?
27072         
27073         this.field = field.el.select('input', true).first();
27074         this.field.on("keydown", this.onPagingKeydown, this);
27075         this.field.on("focus", function(){this.dom.select();});
27076     
27077     
27078         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27079         //this.field.setHeight(18);
27080         //this.addSeparator();
27081         this.next = this.navgroup.addItem({
27082             tooltip: this.nextText,
27083             cls: "next btn-outline-secondary",
27084             html : ' <i class="fa fa-forward"></i>',
27085             disabled: true,
27086             preventDefault: true,
27087             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27088         });
27089         this.last = this.navgroup.addItem({
27090             tooltip: this.lastText,
27091             html : ' <i class="fa fa-step-forward"></i>',
27092             cls: "next btn-outline-secondary",
27093             disabled: true,
27094             preventDefault: true,
27095             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27096         });
27097     //this.addSeparator();
27098         this.loading = this.navgroup.addItem({
27099             tooltip: this.refreshText,
27100             cls: "btn-outline-secondary",
27101             html : ' <i class="fa fa-refresh"></i>',
27102             preventDefault: true,
27103             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27104         });
27105         
27106     },
27107
27108     // private
27109     updateInfo : function(){
27110         if(this.displayEl){
27111             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27112             var msg = count == 0 ?
27113                 this.emptyMsg :
27114                 String.format(
27115                     this.displayMsg,
27116                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27117                 );
27118             this.displayEl.update(msg);
27119         }
27120     },
27121
27122     // private
27123     onLoad : function(ds, r, o)
27124     {
27125         this.cursor = o.params.start ? o.params.start : 0;
27126         
27127         var d = this.getPageData(),
27128             ap = d.activePage,
27129             ps = d.pages;
27130         
27131         
27132         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27133         this.field.dom.value = ap;
27134         this.first.setDisabled(ap == 1);
27135         this.prev.setDisabled(ap == 1);
27136         this.next.setDisabled(ap == ps);
27137         this.last.setDisabled(ap == ps);
27138         this.loading.enable();
27139         this.updateInfo();
27140     },
27141
27142     // private
27143     getPageData : function(){
27144         var total = this.ds.getTotalCount();
27145         return {
27146             total : total,
27147             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27148             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27149         };
27150     },
27151
27152     // private
27153     onLoadError : function(){
27154         this.loading.enable();
27155     },
27156
27157     // private
27158     onPagingKeydown : function(e){
27159         var k = e.getKey();
27160         var d = this.getPageData();
27161         if(k == e.RETURN){
27162             var v = this.field.dom.value, pageNum;
27163             if(!v || isNaN(pageNum = parseInt(v, 10))){
27164                 this.field.dom.value = d.activePage;
27165                 return;
27166             }
27167             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27168             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27169             e.stopEvent();
27170         }
27171         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))
27172         {
27173           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27174           this.field.dom.value = pageNum;
27175           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27176           e.stopEvent();
27177         }
27178         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27179         {
27180           var v = this.field.dom.value, pageNum; 
27181           var increment = (e.shiftKey) ? 10 : 1;
27182           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27183                 increment *= -1;
27184           }
27185           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27186             this.field.dom.value = d.activePage;
27187             return;
27188           }
27189           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27190           {
27191             this.field.dom.value = parseInt(v, 10) + increment;
27192             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27193             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27194           }
27195           e.stopEvent();
27196         }
27197     },
27198
27199     // private
27200     beforeLoad : function(){
27201         if(this.loading){
27202             this.loading.disable();
27203         }
27204     },
27205
27206     // private
27207     onClick : function(which){
27208         
27209         var ds = this.ds;
27210         if (!ds) {
27211             return;
27212         }
27213         
27214         switch(which){
27215             case "first":
27216                 ds.load({params:{start: 0, limit: this.pageSize}});
27217             break;
27218             case "prev":
27219                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27220             break;
27221             case "next":
27222                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27223             break;
27224             case "last":
27225                 var total = ds.getTotalCount();
27226                 var extra = total % this.pageSize;
27227                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27228                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27229             break;
27230             case "refresh":
27231                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27232             break;
27233         }
27234     },
27235
27236     /**
27237      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27238      * @param {Roo.data.Store} store The data store to unbind
27239      */
27240     unbind : function(ds){
27241         ds.un("beforeload", this.beforeLoad, this);
27242         ds.un("load", this.onLoad, this);
27243         ds.un("loadexception", this.onLoadError, this);
27244         ds.un("remove", this.updateInfo, this);
27245         ds.un("add", this.updateInfo, this);
27246         this.ds = undefined;
27247     },
27248
27249     /**
27250      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27251      * @param {Roo.data.Store} store The data store to bind
27252      */
27253     bind : function(ds){
27254         ds.on("beforeload", this.beforeLoad, this);
27255         ds.on("load", this.onLoad, this);
27256         ds.on("loadexception", this.onLoadError, this);
27257         ds.on("remove", this.updateInfo, this);
27258         ds.on("add", this.updateInfo, this);
27259         this.ds = ds;
27260     }
27261 });/*
27262  * - LGPL
27263  *
27264  * element
27265  * 
27266  */
27267
27268 /**
27269  * @class Roo.bootstrap.MessageBar
27270  * @extends Roo.bootstrap.Component
27271  * Bootstrap MessageBar class
27272  * @cfg {String} html contents of the MessageBar
27273  * @cfg {String} weight (info | success | warning | danger) default info
27274  * @cfg {String} beforeClass insert the bar before the given class
27275  * @cfg {Boolean} closable (true | false) default false
27276  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27277  * 
27278  * @constructor
27279  * Create a new Element
27280  * @param {Object} config The config object
27281  */
27282
27283 Roo.bootstrap.MessageBar = function(config){
27284     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27285 };
27286
27287 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27288     
27289     html: '',
27290     weight: 'info',
27291     closable: false,
27292     fixed: false,
27293     beforeClass: 'bootstrap-sticky-wrap',
27294     
27295     getAutoCreate : function(){
27296         
27297         var cfg = {
27298             tag: 'div',
27299             cls: 'alert alert-dismissable alert-' + this.weight,
27300             cn: [
27301                 {
27302                     tag: 'span',
27303                     cls: 'message',
27304                     html: this.html || ''
27305                 }
27306             ]
27307         };
27308         
27309         if(this.fixed){
27310             cfg.cls += ' alert-messages-fixed';
27311         }
27312         
27313         if(this.closable){
27314             cfg.cn.push({
27315                 tag: 'button',
27316                 cls: 'close',
27317                 html: 'x'
27318             });
27319         }
27320         
27321         return cfg;
27322     },
27323     
27324     onRender : function(ct, position)
27325     {
27326         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27327         
27328         if(!this.el){
27329             var cfg = Roo.apply({},  this.getAutoCreate());
27330             cfg.id = Roo.id();
27331             
27332             if (this.cls) {
27333                 cfg.cls += ' ' + this.cls;
27334             }
27335             if (this.style) {
27336                 cfg.style = this.style;
27337             }
27338             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27339             
27340             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27341         }
27342         
27343         this.el.select('>button.close').on('click', this.hide, this);
27344         
27345     },
27346     
27347     show : function()
27348     {
27349         if (!this.rendered) {
27350             this.render();
27351         }
27352         
27353         this.el.show();
27354         
27355         this.fireEvent('show', this);
27356         
27357     },
27358     
27359     hide : function()
27360     {
27361         if (!this.rendered) {
27362             this.render();
27363         }
27364         
27365         this.el.hide();
27366         
27367         this.fireEvent('hide', this);
27368     },
27369     
27370     update : function()
27371     {
27372 //        var e = this.el.dom.firstChild;
27373 //        
27374 //        if(this.closable){
27375 //            e = e.nextSibling;
27376 //        }
27377 //        
27378 //        e.data = this.html || '';
27379
27380         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27381     }
27382    
27383 });
27384
27385  
27386
27387      /*
27388  * - LGPL
27389  *
27390  * Graph
27391  * 
27392  */
27393
27394
27395 /**
27396  * @class Roo.bootstrap.Graph
27397  * @extends Roo.bootstrap.Component
27398  * Bootstrap Graph class
27399 > Prameters
27400  -sm {number} sm 4
27401  -md {number} md 5
27402  @cfg {String} graphtype  bar | vbar | pie
27403  @cfg {number} g_x coodinator | centre x (pie)
27404  @cfg {number} g_y coodinator | centre y (pie)
27405  @cfg {number} g_r radius (pie)
27406  @cfg {number} g_height height of the chart (respected by all elements in the set)
27407  @cfg {number} g_width width of the chart (respected by all elements in the set)
27408  @cfg {Object} title The title of the chart
27409     
27410  -{Array}  values
27411  -opts (object) options for the chart 
27412      o {
27413      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27414      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27415      o vgutter (number)
27416      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.
27417      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27418      o to
27419      o stretch (boolean)
27420      o }
27421  -opts (object) options for the pie
27422      o{
27423      o cut
27424      o startAngle (number)
27425      o endAngle (number)
27426      } 
27427  *
27428  * @constructor
27429  * Create a new Input
27430  * @param {Object} config The config object
27431  */
27432
27433 Roo.bootstrap.Graph = function(config){
27434     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27435     
27436     this.addEvents({
27437         // img events
27438         /**
27439          * @event click
27440          * The img click event for the img.
27441          * @param {Roo.EventObject} e
27442          */
27443         "click" : true
27444     });
27445 };
27446
27447 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27448     
27449     sm: 4,
27450     md: 5,
27451     graphtype: 'bar',
27452     g_height: 250,
27453     g_width: 400,
27454     g_x: 50,
27455     g_y: 50,
27456     g_r: 30,
27457     opts:{
27458         //g_colors: this.colors,
27459         g_type: 'soft',
27460         g_gutter: '20%'
27461
27462     },
27463     title : false,
27464
27465     getAutoCreate : function(){
27466         
27467         var cfg = {
27468             tag: 'div',
27469             html : null
27470         };
27471         
27472         
27473         return  cfg;
27474     },
27475
27476     onRender : function(ct,position){
27477         
27478         
27479         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27480         
27481         if (typeof(Raphael) == 'undefined') {
27482             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27483             return;
27484         }
27485         
27486         this.raphael = Raphael(this.el.dom);
27487         
27488                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27489                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27490                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27491                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27492                 /*
27493                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27494                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27495                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27496                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27497                 
27498                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27499                 r.barchart(330, 10, 300, 220, data1);
27500                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27501                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27502                 */
27503                 
27504                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27505                 // r.barchart(30, 30, 560, 250,  xdata, {
27506                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27507                 //     axis : "0 0 1 1",
27508                 //     axisxlabels :  xdata
27509                 //     //yvalues : cols,
27510                    
27511                 // });
27512 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27513 //        
27514 //        this.load(null,xdata,{
27515 //                axis : "0 0 1 1",
27516 //                axisxlabels :  xdata
27517 //                });
27518
27519     },
27520
27521     load : function(graphtype,xdata,opts)
27522     {
27523         this.raphael.clear();
27524         if(!graphtype) {
27525             graphtype = this.graphtype;
27526         }
27527         if(!opts){
27528             opts = this.opts;
27529         }
27530         var r = this.raphael,
27531             fin = function () {
27532                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27533             },
27534             fout = function () {
27535                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27536             },
27537             pfin = function() {
27538                 this.sector.stop();
27539                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27540
27541                 if (this.label) {
27542                     this.label[0].stop();
27543                     this.label[0].attr({ r: 7.5 });
27544                     this.label[1].attr({ "font-weight": 800 });
27545                 }
27546             },
27547             pfout = function() {
27548                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27549
27550                 if (this.label) {
27551                     this.label[0].animate({ r: 5 }, 500, "bounce");
27552                     this.label[1].attr({ "font-weight": 400 });
27553                 }
27554             };
27555
27556         switch(graphtype){
27557             case 'bar':
27558                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27559                 break;
27560             case 'hbar':
27561                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27562                 break;
27563             case 'pie':
27564 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27565 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27566 //            
27567                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27568                 
27569                 break;
27570
27571         }
27572         
27573         if(this.title){
27574             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27575         }
27576         
27577     },
27578     
27579     setTitle: function(o)
27580     {
27581         this.title = o;
27582     },
27583     
27584     initEvents: function() {
27585         
27586         if(!this.href){
27587             this.el.on('click', this.onClick, this);
27588         }
27589     },
27590     
27591     onClick : function(e)
27592     {
27593         Roo.log('img onclick');
27594         this.fireEvent('click', this, e);
27595     }
27596    
27597 });
27598
27599  
27600 /*
27601  * - LGPL
27602  *
27603  * numberBox
27604  * 
27605  */
27606 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27607
27608 /**
27609  * @class Roo.bootstrap.dash.NumberBox
27610  * @extends Roo.bootstrap.Component
27611  * Bootstrap NumberBox class
27612  * @cfg {String} headline Box headline
27613  * @cfg {String} content Box content
27614  * @cfg {String} icon Box icon
27615  * @cfg {String} footer Footer text
27616  * @cfg {String} fhref Footer href
27617  * 
27618  * @constructor
27619  * Create a new NumberBox
27620  * @param {Object} config The config object
27621  */
27622
27623
27624 Roo.bootstrap.dash.NumberBox = function(config){
27625     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27626     
27627 };
27628
27629 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27630     
27631     headline : '',
27632     content : '',
27633     icon : '',
27634     footer : '',
27635     fhref : '',
27636     ficon : '',
27637     
27638     getAutoCreate : function(){
27639         
27640         var cfg = {
27641             tag : 'div',
27642             cls : 'small-box ',
27643             cn : [
27644                 {
27645                     tag : 'div',
27646                     cls : 'inner',
27647                     cn :[
27648                         {
27649                             tag : 'h3',
27650                             cls : 'roo-headline',
27651                             html : this.headline
27652                         },
27653                         {
27654                             tag : 'p',
27655                             cls : 'roo-content',
27656                             html : this.content
27657                         }
27658                     ]
27659                 }
27660             ]
27661         };
27662         
27663         if(this.icon){
27664             cfg.cn.push({
27665                 tag : 'div',
27666                 cls : 'icon',
27667                 cn :[
27668                     {
27669                         tag : 'i',
27670                         cls : 'ion ' + this.icon
27671                     }
27672                 ]
27673             });
27674         }
27675         
27676         if(this.footer){
27677             var footer = {
27678                 tag : 'a',
27679                 cls : 'small-box-footer',
27680                 href : this.fhref || '#',
27681                 html : this.footer
27682             };
27683             
27684             cfg.cn.push(footer);
27685             
27686         }
27687         
27688         return  cfg;
27689     },
27690
27691     onRender : function(ct,position){
27692         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27693
27694
27695        
27696                 
27697     },
27698
27699     setHeadline: function (value)
27700     {
27701         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27702     },
27703     
27704     setFooter: function (value, href)
27705     {
27706         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27707         
27708         if(href){
27709             this.el.select('a.small-box-footer',true).first().attr('href', href);
27710         }
27711         
27712     },
27713
27714     setContent: function (value)
27715     {
27716         this.el.select('.roo-content',true).first().dom.innerHTML = value;
27717     },
27718
27719     initEvents: function() 
27720     {   
27721         
27722     }
27723     
27724 });
27725
27726  
27727 /*
27728  * - LGPL
27729  *
27730  * TabBox
27731  * 
27732  */
27733 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27734
27735 /**
27736  * @class Roo.bootstrap.dash.TabBox
27737  * @extends Roo.bootstrap.Component
27738  * Bootstrap TabBox class
27739  * @cfg {String} title Title of the TabBox
27740  * @cfg {String} icon Icon of the TabBox
27741  * @cfg {Boolean} showtabs (true|false) show the tabs default true
27742  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27743  * 
27744  * @constructor
27745  * Create a new TabBox
27746  * @param {Object} config The config object
27747  */
27748
27749
27750 Roo.bootstrap.dash.TabBox = function(config){
27751     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27752     this.addEvents({
27753         // raw events
27754         /**
27755          * @event addpane
27756          * When a pane is added
27757          * @param {Roo.bootstrap.dash.TabPane} pane
27758          */
27759         "addpane" : true,
27760         /**
27761          * @event activatepane
27762          * When a pane is activated
27763          * @param {Roo.bootstrap.dash.TabPane} pane
27764          */
27765         "activatepane" : true
27766         
27767          
27768     });
27769     
27770     this.panes = [];
27771 };
27772
27773 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
27774
27775     title : '',
27776     icon : false,
27777     showtabs : true,
27778     tabScrollable : false,
27779     
27780     getChildContainer : function()
27781     {
27782         return this.el.select('.tab-content', true).first();
27783     },
27784     
27785     getAutoCreate : function(){
27786         
27787         var header = {
27788             tag: 'li',
27789             cls: 'pull-left header',
27790             html: this.title,
27791             cn : []
27792         };
27793         
27794         if(this.icon){
27795             header.cn.push({
27796                 tag: 'i',
27797                 cls: 'fa ' + this.icon
27798             });
27799         }
27800         
27801         var h = {
27802             tag: 'ul',
27803             cls: 'nav nav-tabs pull-right',
27804             cn: [
27805                 header
27806             ]
27807         };
27808         
27809         if(this.tabScrollable){
27810             h = {
27811                 tag: 'div',
27812                 cls: 'tab-header',
27813                 cn: [
27814                     {
27815                         tag: 'ul',
27816                         cls: 'nav nav-tabs pull-right',
27817                         cn: [
27818                             header
27819                         ]
27820                     }
27821                 ]
27822             };
27823         }
27824         
27825         var cfg = {
27826             tag: 'div',
27827             cls: 'nav-tabs-custom',
27828             cn: [
27829                 h,
27830                 {
27831                     tag: 'div',
27832                     cls: 'tab-content no-padding',
27833                     cn: []
27834                 }
27835             ]
27836         };
27837
27838         return  cfg;
27839     },
27840     initEvents : function()
27841     {
27842         //Roo.log('add add pane handler');
27843         this.on('addpane', this.onAddPane, this);
27844     },
27845      /**
27846      * Updates the box title
27847      * @param {String} html to set the title to.
27848      */
27849     setTitle : function(value)
27850     {
27851         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
27852     },
27853     onAddPane : function(pane)
27854     {
27855         this.panes.push(pane);
27856         //Roo.log('addpane');
27857         //Roo.log(pane);
27858         // tabs are rendere left to right..
27859         if(!this.showtabs){
27860             return;
27861         }
27862         
27863         var ctr = this.el.select('.nav-tabs', true).first();
27864          
27865          
27866         var existing = ctr.select('.nav-tab',true);
27867         var qty = existing.getCount();;
27868         
27869         
27870         var tab = ctr.createChild({
27871             tag : 'li',
27872             cls : 'nav-tab' + (qty ? '' : ' active'),
27873             cn : [
27874                 {
27875                     tag : 'a',
27876                     href:'#',
27877                     html : pane.title
27878                 }
27879             ]
27880         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
27881         pane.tab = tab;
27882         
27883         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
27884         if (!qty) {
27885             pane.el.addClass('active');
27886         }
27887         
27888                 
27889     },
27890     onTabClick : function(ev,un,ob,pane)
27891     {
27892         //Roo.log('tab - prev default');
27893         ev.preventDefault();
27894         
27895         
27896         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
27897         pane.tab.addClass('active');
27898         //Roo.log(pane.title);
27899         this.getChildContainer().select('.tab-pane',true).removeClass('active');
27900         // technically we should have a deactivate event.. but maybe add later.
27901         // and it should not de-activate the selected tab...
27902         this.fireEvent('activatepane', pane);
27903         pane.el.addClass('active');
27904         pane.fireEvent('activate');
27905         
27906         
27907     },
27908     
27909     getActivePane : function()
27910     {
27911         var r = false;
27912         Roo.each(this.panes, function(p) {
27913             if(p.el.hasClass('active')){
27914                 r = p;
27915                 return false;
27916             }
27917             
27918             return;
27919         });
27920         
27921         return r;
27922     }
27923     
27924     
27925 });
27926
27927  
27928 /*
27929  * - LGPL
27930  *
27931  * Tab pane
27932  * 
27933  */
27934 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27935 /**
27936  * @class Roo.bootstrap.TabPane
27937  * @extends Roo.bootstrap.Component
27938  * Bootstrap TabPane class
27939  * @cfg {Boolean} active (false | true) Default false
27940  * @cfg {String} title title of panel
27941
27942  * 
27943  * @constructor
27944  * Create a new TabPane
27945  * @param {Object} config The config object
27946  */
27947
27948 Roo.bootstrap.dash.TabPane = function(config){
27949     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
27950     
27951     this.addEvents({
27952         // raw events
27953         /**
27954          * @event activate
27955          * When a pane is activated
27956          * @param {Roo.bootstrap.dash.TabPane} pane
27957          */
27958         "activate" : true
27959          
27960     });
27961 };
27962
27963 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
27964     
27965     active : false,
27966     title : '',
27967     
27968     // the tabBox that this is attached to.
27969     tab : false,
27970      
27971     getAutoCreate : function() 
27972     {
27973         var cfg = {
27974             tag: 'div',
27975             cls: 'tab-pane'
27976         };
27977         
27978         if(this.active){
27979             cfg.cls += ' active';
27980         }
27981         
27982         return cfg;
27983     },
27984     initEvents  : function()
27985     {
27986         //Roo.log('trigger add pane handler');
27987         this.parent().fireEvent('addpane', this)
27988     },
27989     
27990      /**
27991      * Updates the tab title 
27992      * @param {String} html to set the title to.
27993      */
27994     setTitle: function(str)
27995     {
27996         if (!this.tab) {
27997             return;
27998         }
27999         this.title = str;
28000         this.tab.select('a', true).first().dom.innerHTML = str;
28001         
28002     }
28003     
28004     
28005     
28006 });
28007
28008  
28009
28010
28011  /*
28012  * - LGPL
28013  *
28014  * menu
28015  * 
28016  */
28017 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28018
28019 /**
28020  * @class Roo.bootstrap.menu.Menu
28021  * @extends Roo.bootstrap.Component
28022  * Bootstrap Menu class - container for Menu
28023  * @cfg {String} html Text of the menu
28024  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28025  * @cfg {String} icon Font awesome icon
28026  * @cfg {String} pos Menu align to (top | bottom) default bottom
28027  * 
28028  * 
28029  * @constructor
28030  * Create a new Menu
28031  * @param {Object} config The config object
28032  */
28033
28034
28035 Roo.bootstrap.menu.Menu = function(config){
28036     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28037     
28038     this.addEvents({
28039         /**
28040          * @event beforeshow
28041          * Fires before this menu is displayed
28042          * @param {Roo.bootstrap.menu.Menu} this
28043          */
28044         beforeshow : true,
28045         /**
28046          * @event beforehide
28047          * Fires before this menu is hidden
28048          * @param {Roo.bootstrap.menu.Menu} this
28049          */
28050         beforehide : true,
28051         /**
28052          * @event show
28053          * Fires after this menu is displayed
28054          * @param {Roo.bootstrap.menu.Menu} this
28055          */
28056         show : true,
28057         /**
28058          * @event hide
28059          * Fires after this menu is hidden
28060          * @param {Roo.bootstrap.menu.Menu} this
28061          */
28062         hide : true,
28063         /**
28064          * @event click
28065          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28066          * @param {Roo.bootstrap.menu.Menu} this
28067          * @param {Roo.EventObject} e
28068          */
28069         click : true
28070     });
28071     
28072 };
28073
28074 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28075     
28076     submenu : false,
28077     html : '',
28078     weight : 'default',
28079     icon : false,
28080     pos : 'bottom',
28081     
28082     
28083     getChildContainer : function() {
28084         if(this.isSubMenu){
28085             return this.el;
28086         }
28087         
28088         return this.el.select('ul.dropdown-menu', true).first();  
28089     },
28090     
28091     getAutoCreate : function()
28092     {
28093         var text = [
28094             {
28095                 tag : 'span',
28096                 cls : 'roo-menu-text',
28097                 html : this.html
28098             }
28099         ];
28100         
28101         if(this.icon){
28102             text.unshift({
28103                 tag : 'i',
28104                 cls : 'fa ' + this.icon
28105             })
28106         }
28107         
28108         
28109         var cfg = {
28110             tag : 'div',
28111             cls : 'btn-group',
28112             cn : [
28113                 {
28114                     tag : 'button',
28115                     cls : 'dropdown-button btn btn-' + this.weight,
28116                     cn : text
28117                 },
28118                 {
28119                     tag : 'button',
28120                     cls : 'dropdown-toggle btn btn-' + this.weight,
28121                     cn : [
28122                         {
28123                             tag : 'span',
28124                             cls : 'caret'
28125                         }
28126                     ]
28127                 },
28128                 {
28129                     tag : 'ul',
28130                     cls : 'dropdown-menu'
28131                 }
28132             ]
28133             
28134         };
28135         
28136         if(this.pos == 'top'){
28137             cfg.cls += ' dropup';
28138         }
28139         
28140         if(this.isSubMenu){
28141             cfg = {
28142                 tag : 'ul',
28143                 cls : 'dropdown-menu'
28144             }
28145         }
28146         
28147         return cfg;
28148     },
28149     
28150     onRender : function(ct, position)
28151     {
28152         this.isSubMenu = ct.hasClass('dropdown-submenu');
28153         
28154         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28155     },
28156     
28157     initEvents : function() 
28158     {
28159         if(this.isSubMenu){
28160             return;
28161         }
28162         
28163         this.hidden = true;
28164         
28165         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28166         this.triggerEl.on('click', this.onTriggerPress, this);
28167         
28168         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28169         this.buttonEl.on('click', this.onClick, this);
28170         
28171     },
28172     
28173     list : function()
28174     {
28175         if(this.isSubMenu){
28176             return this.el;
28177         }
28178         
28179         return this.el.select('ul.dropdown-menu', true).first();
28180     },
28181     
28182     onClick : function(e)
28183     {
28184         this.fireEvent("click", this, e);
28185     },
28186     
28187     onTriggerPress  : function(e)
28188     {   
28189         if (this.isVisible()) {
28190             this.hide();
28191         } else {
28192             this.show();
28193         }
28194     },
28195     
28196     isVisible : function(){
28197         return !this.hidden;
28198     },
28199     
28200     show : function()
28201     {
28202         this.fireEvent("beforeshow", this);
28203         
28204         this.hidden = false;
28205         this.el.addClass('open');
28206         
28207         Roo.get(document).on("mouseup", this.onMouseUp, this);
28208         
28209         this.fireEvent("show", this);
28210         
28211         
28212     },
28213     
28214     hide : function()
28215     {
28216         this.fireEvent("beforehide", this);
28217         
28218         this.hidden = true;
28219         this.el.removeClass('open');
28220         
28221         Roo.get(document).un("mouseup", this.onMouseUp);
28222         
28223         this.fireEvent("hide", this);
28224     },
28225     
28226     onMouseUp : function()
28227     {
28228         this.hide();
28229     }
28230     
28231 });
28232
28233  
28234  /*
28235  * - LGPL
28236  *
28237  * menu item
28238  * 
28239  */
28240 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28241
28242 /**
28243  * @class Roo.bootstrap.menu.Item
28244  * @extends Roo.bootstrap.Component
28245  * Bootstrap MenuItem class
28246  * @cfg {Boolean} submenu (true | false) default false
28247  * @cfg {String} html text of the item
28248  * @cfg {String} href the link
28249  * @cfg {Boolean} disable (true | false) default false
28250  * @cfg {Boolean} preventDefault (true | false) default true
28251  * @cfg {String} icon Font awesome icon
28252  * @cfg {String} pos Submenu align to (left | right) default right 
28253  * 
28254  * 
28255  * @constructor
28256  * Create a new Item
28257  * @param {Object} config The config object
28258  */
28259
28260
28261 Roo.bootstrap.menu.Item = function(config){
28262     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28263     this.addEvents({
28264         /**
28265          * @event mouseover
28266          * Fires when the mouse is hovering over this menu
28267          * @param {Roo.bootstrap.menu.Item} this
28268          * @param {Roo.EventObject} e
28269          */
28270         mouseover : true,
28271         /**
28272          * @event mouseout
28273          * Fires when the mouse exits this menu
28274          * @param {Roo.bootstrap.menu.Item} this
28275          * @param {Roo.EventObject} e
28276          */
28277         mouseout : true,
28278         // raw events
28279         /**
28280          * @event click
28281          * The raw click event for the entire grid.
28282          * @param {Roo.EventObject} e
28283          */
28284         click : true
28285     });
28286 };
28287
28288 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28289     
28290     submenu : false,
28291     href : '',
28292     html : '',
28293     preventDefault: true,
28294     disable : false,
28295     icon : false,
28296     pos : 'right',
28297     
28298     getAutoCreate : function()
28299     {
28300         var text = [
28301             {
28302                 tag : 'span',
28303                 cls : 'roo-menu-item-text',
28304                 html : this.html
28305             }
28306         ];
28307         
28308         if(this.icon){
28309             text.unshift({
28310                 tag : 'i',
28311                 cls : 'fa ' + this.icon
28312             })
28313         }
28314         
28315         var cfg = {
28316             tag : 'li',
28317             cn : [
28318                 {
28319                     tag : 'a',
28320                     href : this.href || '#',
28321                     cn : text
28322                 }
28323             ]
28324         };
28325         
28326         if(this.disable){
28327             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28328         }
28329         
28330         if(this.submenu){
28331             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28332             
28333             if(this.pos == 'left'){
28334                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28335             }
28336         }
28337         
28338         return cfg;
28339     },
28340     
28341     initEvents : function() 
28342     {
28343         this.el.on('mouseover', this.onMouseOver, this);
28344         this.el.on('mouseout', this.onMouseOut, this);
28345         
28346         this.el.select('a', true).first().on('click', this.onClick, this);
28347         
28348     },
28349     
28350     onClick : function(e)
28351     {
28352         if(this.preventDefault){
28353             e.preventDefault();
28354         }
28355         
28356         this.fireEvent("click", this, e);
28357     },
28358     
28359     onMouseOver : function(e)
28360     {
28361         if(this.submenu && this.pos == 'left'){
28362             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28363         }
28364         
28365         this.fireEvent("mouseover", this, e);
28366     },
28367     
28368     onMouseOut : function(e)
28369     {
28370         this.fireEvent("mouseout", this, e);
28371     }
28372 });
28373
28374  
28375
28376  /*
28377  * - LGPL
28378  *
28379  * menu separator
28380  * 
28381  */
28382 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28383
28384 /**
28385  * @class Roo.bootstrap.menu.Separator
28386  * @extends Roo.bootstrap.Component
28387  * Bootstrap Separator class
28388  * 
28389  * @constructor
28390  * Create a new Separator
28391  * @param {Object} config The config object
28392  */
28393
28394
28395 Roo.bootstrap.menu.Separator = function(config){
28396     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28397 };
28398
28399 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28400     
28401     getAutoCreate : function(){
28402         var cfg = {
28403             tag : 'li',
28404             cls: 'divider'
28405         };
28406         
28407         return cfg;
28408     }
28409    
28410 });
28411
28412  
28413
28414  /*
28415  * - LGPL
28416  *
28417  * Tooltip
28418  * 
28419  */
28420
28421 /**
28422  * @class Roo.bootstrap.Tooltip
28423  * Bootstrap Tooltip class
28424  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28425  * to determine which dom element triggers the tooltip.
28426  * 
28427  * It needs to add support for additional attributes like tooltip-position
28428  * 
28429  * @constructor
28430  * Create a new Toolti
28431  * @param {Object} config The config object
28432  */
28433
28434 Roo.bootstrap.Tooltip = function(config){
28435     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28436     
28437     this.alignment = Roo.bootstrap.Tooltip.alignment;
28438     
28439     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28440         this.alignment = config.alignment;
28441     }
28442     
28443 };
28444
28445 Roo.apply(Roo.bootstrap.Tooltip, {
28446     /**
28447      * @function init initialize tooltip monitoring.
28448      * @static
28449      */
28450     currentEl : false,
28451     currentTip : false,
28452     currentRegion : false,
28453     
28454     //  init : delay?
28455     
28456     init : function()
28457     {
28458         Roo.get(document).on('mouseover', this.enter ,this);
28459         Roo.get(document).on('mouseout', this.leave, this);
28460          
28461         
28462         this.currentTip = new Roo.bootstrap.Tooltip();
28463     },
28464     
28465     enter : function(ev)
28466     {
28467         var dom = ev.getTarget();
28468         
28469         //Roo.log(['enter',dom]);
28470         var el = Roo.fly(dom);
28471         if (this.currentEl) {
28472             //Roo.log(dom);
28473             //Roo.log(this.currentEl);
28474             //Roo.log(this.currentEl.contains(dom));
28475             if (this.currentEl == el) {
28476                 return;
28477             }
28478             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28479                 return;
28480             }
28481
28482         }
28483         
28484         if (this.currentTip.el) {
28485             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28486         }    
28487         //Roo.log(ev);
28488         
28489         if(!el || el.dom == document){
28490             return;
28491         }
28492         
28493         var bindEl = el;
28494         
28495         // you can not look for children, as if el is the body.. then everythign is the child..
28496         if (!el.attr('tooltip')) { //
28497             if (!el.select("[tooltip]").elements.length) {
28498                 return;
28499             }
28500             // is the mouse over this child...?
28501             bindEl = el.select("[tooltip]").first();
28502             var xy = ev.getXY();
28503             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28504                 //Roo.log("not in region.");
28505                 return;
28506             }
28507             //Roo.log("child element over..");
28508             
28509         }
28510         this.currentEl = bindEl;
28511         this.currentTip.bind(bindEl);
28512         this.currentRegion = Roo.lib.Region.getRegion(dom);
28513         this.currentTip.enter();
28514         
28515     },
28516     leave : function(ev)
28517     {
28518         var dom = ev.getTarget();
28519         //Roo.log(['leave',dom]);
28520         if (!this.currentEl) {
28521             return;
28522         }
28523         
28524         
28525         if (dom != this.currentEl.dom) {
28526             return;
28527         }
28528         var xy = ev.getXY();
28529         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28530             return;
28531         }
28532         // only activate leave if mouse cursor is outside... bounding box..
28533         
28534         
28535         
28536         
28537         if (this.currentTip) {
28538             this.currentTip.leave();
28539         }
28540         //Roo.log('clear currentEl');
28541         this.currentEl = false;
28542         
28543         
28544     },
28545     alignment : {
28546         'left' : ['r-l', [-2,0], 'right'],
28547         'right' : ['l-r', [2,0], 'left'],
28548         'bottom' : ['t-b', [0,2], 'top'],
28549         'top' : [ 'b-t', [0,-2], 'bottom']
28550     }
28551     
28552 });
28553
28554
28555 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28556     
28557     
28558     bindEl : false,
28559     
28560     delay : null, // can be { show : 300 , hide: 500}
28561     
28562     timeout : null,
28563     
28564     hoverState : null, //???
28565     
28566     placement : 'bottom', 
28567     
28568     alignment : false,
28569     
28570     getAutoCreate : function(){
28571     
28572         var cfg = {
28573            cls : 'tooltip',   
28574            role : 'tooltip',
28575            cn : [
28576                 {
28577                     cls : 'tooltip-arrow arrow'
28578                 },
28579                 {
28580                     cls : 'tooltip-inner'
28581                 }
28582            ]
28583         };
28584         
28585         return cfg;
28586     },
28587     bind : function(el)
28588     {
28589         this.bindEl = el;
28590     },
28591     
28592     initEvents : function()
28593     {
28594         this.arrowEl = this.el.select('.arrow', true).first();
28595         this.innerEl = this.el.select('.tooltip-inner', true).first();
28596     },
28597     
28598     enter : function () {
28599        
28600         if (this.timeout != null) {
28601             clearTimeout(this.timeout);
28602         }
28603         
28604         this.hoverState = 'in';
28605          //Roo.log("enter - show");
28606         if (!this.delay || !this.delay.show) {
28607             this.show();
28608             return;
28609         }
28610         var _t = this;
28611         this.timeout = setTimeout(function () {
28612             if (_t.hoverState == 'in') {
28613                 _t.show();
28614             }
28615         }, this.delay.show);
28616     },
28617     leave : function()
28618     {
28619         clearTimeout(this.timeout);
28620     
28621         this.hoverState = 'out';
28622          if (!this.delay || !this.delay.hide) {
28623             this.hide();
28624             return;
28625         }
28626        
28627         var _t = this;
28628         this.timeout = setTimeout(function () {
28629             //Roo.log("leave - timeout");
28630             
28631             if (_t.hoverState == 'out') {
28632                 _t.hide();
28633                 Roo.bootstrap.Tooltip.currentEl = false;
28634             }
28635         }, delay);
28636     },
28637     
28638     show : function (msg)
28639     {
28640         if (!this.el) {
28641             this.render(document.body);
28642         }
28643         // set content.
28644         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28645         
28646         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28647         
28648         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28649         
28650         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28651                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28652         
28653         var placement = typeof this.placement == 'function' ?
28654             this.placement.call(this, this.el, on_el) :
28655             this.placement;
28656             
28657         var autoToken = /\s?auto?\s?/i;
28658         var autoPlace = autoToken.test(placement);
28659         if (autoPlace) {
28660             placement = placement.replace(autoToken, '') || 'top';
28661         }
28662         
28663         //this.el.detach()
28664         //this.el.setXY([0,0]);
28665         this.el.show();
28666         //this.el.dom.style.display='block';
28667         
28668         //this.el.appendTo(on_el);
28669         
28670         var p = this.getPosition();
28671         var box = this.el.getBox();
28672         
28673         if (autoPlace) {
28674             // fixme..
28675         }
28676         
28677         var align = this.alignment[placement];
28678         
28679         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28680         
28681         if(placement == 'top' || placement == 'bottom'){
28682             if(xy[0] < 0){
28683                 placement = 'right';
28684             }
28685             
28686             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28687                 placement = 'left';
28688             }
28689             
28690             var scroll = Roo.select('body', true).first().getScroll();
28691             
28692             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28693                 placement = 'top';
28694             }
28695             
28696             align = this.alignment[placement];
28697             
28698             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28699             
28700         }
28701         
28702         this.el.alignTo(this.bindEl, align[0],align[1]);
28703         //var arrow = this.el.select('.arrow',true).first();
28704         //arrow.set(align[2], 
28705         
28706         this.el.addClass(placement);
28707         this.el.addClass("bs-tooltip-"+ placement);
28708         
28709         this.el.addClass('in fade show');
28710         
28711         this.hoverState = null;
28712         
28713         if (this.el.hasClass('fade')) {
28714             // fade it?
28715         }
28716         
28717         
28718         
28719         
28720         
28721     },
28722     hide : function()
28723     {
28724          
28725         if (!this.el) {
28726             return;
28727         }
28728         //this.el.setXY([0,0]);
28729         this.el.removeClass(['show', 'in']);
28730         //this.el.hide();
28731         
28732     }
28733     
28734 });
28735  
28736
28737  /*
28738  * - LGPL
28739  *
28740  * Location Picker
28741  * 
28742  */
28743
28744 /**
28745  * @class Roo.bootstrap.LocationPicker
28746  * @extends Roo.bootstrap.Component
28747  * Bootstrap LocationPicker class
28748  * @cfg {Number} latitude Position when init default 0
28749  * @cfg {Number} longitude Position when init default 0
28750  * @cfg {Number} zoom default 15
28751  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28752  * @cfg {Boolean} mapTypeControl default false
28753  * @cfg {Boolean} disableDoubleClickZoom default false
28754  * @cfg {Boolean} scrollwheel default true
28755  * @cfg {Boolean} streetViewControl default false
28756  * @cfg {Number} radius default 0
28757  * @cfg {String} locationName
28758  * @cfg {Boolean} draggable default true
28759  * @cfg {Boolean} enableAutocomplete default false
28760  * @cfg {Boolean} enableReverseGeocode default true
28761  * @cfg {String} markerTitle
28762  * 
28763  * @constructor
28764  * Create a new LocationPicker
28765  * @param {Object} config The config object
28766  */
28767
28768
28769 Roo.bootstrap.LocationPicker = function(config){
28770     
28771     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28772     
28773     this.addEvents({
28774         /**
28775          * @event initial
28776          * Fires when the picker initialized.
28777          * @param {Roo.bootstrap.LocationPicker} this
28778          * @param {Google Location} location
28779          */
28780         initial : true,
28781         /**
28782          * @event positionchanged
28783          * Fires when the picker position changed.
28784          * @param {Roo.bootstrap.LocationPicker} this
28785          * @param {Google Location} location
28786          */
28787         positionchanged : true,
28788         /**
28789          * @event resize
28790          * Fires when the map resize.
28791          * @param {Roo.bootstrap.LocationPicker} this
28792          */
28793         resize : true,
28794         /**
28795          * @event show
28796          * Fires when the map show.
28797          * @param {Roo.bootstrap.LocationPicker} this
28798          */
28799         show : true,
28800         /**
28801          * @event hide
28802          * Fires when the map hide.
28803          * @param {Roo.bootstrap.LocationPicker} this
28804          */
28805         hide : true,
28806         /**
28807          * @event mapClick
28808          * Fires when click the map.
28809          * @param {Roo.bootstrap.LocationPicker} this
28810          * @param {Map event} e
28811          */
28812         mapClick : true,
28813         /**
28814          * @event mapRightClick
28815          * Fires when right click the map.
28816          * @param {Roo.bootstrap.LocationPicker} this
28817          * @param {Map event} e
28818          */
28819         mapRightClick : true,
28820         /**
28821          * @event markerClick
28822          * Fires when click the marker.
28823          * @param {Roo.bootstrap.LocationPicker} this
28824          * @param {Map event} e
28825          */
28826         markerClick : true,
28827         /**
28828          * @event markerRightClick
28829          * Fires when right click the marker.
28830          * @param {Roo.bootstrap.LocationPicker} this
28831          * @param {Map event} e
28832          */
28833         markerRightClick : true,
28834         /**
28835          * @event OverlayViewDraw
28836          * Fires when OverlayView Draw
28837          * @param {Roo.bootstrap.LocationPicker} this
28838          */
28839         OverlayViewDraw : true,
28840         /**
28841          * @event OverlayViewOnAdd
28842          * Fires when OverlayView Draw
28843          * @param {Roo.bootstrap.LocationPicker} this
28844          */
28845         OverlayViewOnAdd : true,
28846         /**
28847          * @event OverlayViewOnRemove
28848          * Fires when OverlayView Draw
28849          * @param {Roo.bootstrap.LocationPicker} this
28850          */
28851         OverlayViewOnRemove : true,
28852         /**
28853          * @event OverlayViewShow
28854          * Fires when OverlayView Draw
28855          * @param {Roo.bootstrap.LocationPicker} this
28856          * @param {Pixel} cpx
28857          */
28858         OverlayViewShow : true,
28859         /**
28860          * @event OverlayViewHide
28861          * Fires when OverlayView Draw
28862          * @param {Roo.bootstrap.LocationPicker} this
28863          */
28864         OverlayViewHide : true,
28865         /**
28866          * @event loadexception
28867          * Fires when load google lib failed.
28868          * @param {Roo.bootstrap.LocationPicker} this
28869          */
28870         loadexception : true
28871     });
28872         
28873 };
28874
28875 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
28876     
28877     gMapContext: false,
28878     
28879     latitude: 0,
28880     longitude: 0,
28881     zoom: 15,
28882     mapTypeId: false,
28883     mapTypeControl: false,
28884     disableDoubleClickZoom: false,
28885     scrollwheel: true,
28886     streetViewControl: false,
28887     radius: 0,
28888     locationName: '',
28889     draggable: true,
28890     enableAutocomplete: false,
28891     enableReverseGeocode: true,
28892     markerTitle: '',
28893     
28894     getAutoCreate: function()
28895     {
28896
28897         var cfg = {
28898             tag: 'div',
28899             cls: 'roo-location-picker'
28900         };
28901         
28902         return cfg
28903     },
28904     
28905     initEvents: function(ct, position)
28906     {       
28907         if(!this.el.getWidth() || this.isApplied()){
28908             return;
28909         }
28910         
28911         this.el.setVisibilityMode(Roo.Element.DISPLAY);
28912         
28913         this.initial();
28914     },
28915     
28916     initial: function()
28917     {
28918         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
28919             this.fireEvent('loadexception', this);
28920             return;
28921         }
28922         
28923         if(!this.mapTypeId){
28924             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
28925         }
28926         
28927         this.gMapContext = this.GMapContext();
28928         
28929         this.initOverlayView();
28930         
28931         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
28932         
28933         var _this = this;
28934                 
28935         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
28936             _this.setPosition(_this.gMapContext.marker.position);
28937         });
28938         
28939         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
28940             _this.fireEvent('mapClick', this, event);
28941             
28942         });
28943
28944         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
28945             _this.fireEvent('mapRightClick', this, event);
28946             
28947         });
28948         
28949         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
28950             _this.fireEvent('markerClick', this, event);
28951             
28952         });
28953
28954         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
28955             _this.fireEvent('markerRightClick', this, event);
28956             
28957         });
28958         
28959         this.setPosition(this.gMapContext.location);
28960         
28961         this.fireEvent('initial', this, this.gMapContext.location);
28962     },
28963     
28964     initOverlayView: function()
28965     {
28966         var _this = this;
28967         
28968         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
28969             
28970             draw: function()
28971             {
28972                 _this.fireEvent('OverlayViewDraw', _this);
28973             },
28974             
28975             onAdd: function()
28976             {
28977                 _this.fireEvent('OverlayViewOnAdd', _this);
28978             },
28979             
28980             onRemove: function()
28981             {
28982                 _this.fireEvent('OverlayViewOnRemove', _this);
28983             },
28984             
28985             show: function(cpx)
28986             {
28987                 _this.fireEvent('OverlayViewShow', _this, cpx);
28988             },
28989             
28990             hide: function()
28991             {
28992                 _this.fireEvent('OverlayViewHide', _this);
28993             }
28994             
28995         });
28996     },
28997     
28998     fromLatLngToContainerPixel: function(event)
28999     {
29000         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29001     },
29002     
29003     isApplied: function() 
29004     {
29005         return this.getGmapContext() == false ? false : true;
29006     },
29007     
29008     getGmapContext: function() 
29009     {
29010         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29011     },
29012     
29013     GMapContext: function() 
29014     {
29015         var position = new google.maps.LatLng(this.latitude, this.longitude);
29016         
29017         var _map = new google.maps.Map(this.el.dom, {
29018             center: position,
29019             zoom: this.zoom,
29020             mapTypeId: this.mapTypeId,
29021             mapTypeControl: this.mapTypeControl,
29022             disableDoubleClickZoom: this.disableDoubleClickZoom,
29023             scrollwheel: this.scrollwheel,
29024             streetViewControl: this.streetViewControl,
29025             locationName: this.locationName,
29026             draggable: this.draggable,
29027             enableAutocomplete: this.enableAutocomplete,
29028             enableReverseGeocode: this.enableReverseGeocode
29029         });
29030         
29031         var _marker = new google.maps.Marker({
29032             position: position,
29033             map: _map,
29034             title: this.markerTitle,
29035             draggable: this.draggable
29036         });
29037         
29038         return {
29039             map: _map,
29040             marker: _marker,
29041             circle: null,
29042             location: position,
29043             radius: this.radius,
29044             locationName: this.locationName,
29045             addressComponents: {
29046                 formatted_address: null,
29047                 addressLine1: null,
29048                 addressLine2: null,
29049                 streetName: null,
29050                 streetNumber: null,
29051                 city: null,
29052                 district: null,
29053                 state: null,
29054                 stateOrProvince: null
29055             },
29056             settings: this,
29057             domContainer: this.el.dom,
29058             geodecoder: new google.maps.Geocoder()
29059         };
29060     },
29061     
29062     drawCircle: function(center, radius, options) 
29063     {
29064         if (this.gMapContext.circle != null) {
29065             this.gMapContext.circle.setMap(null);
29066         }
29067         if (radius > 0) {
29068             radius *= 1;
29069             options = Roo.apply({}, options, {
29070                 strokeColor: "#0000FF",
29071                 strokeOpacity: .35,
29072                 strokeWeight: 2,
29073                 fillColor: "#0000FF",
29074                 fillOpacity: .2
29075             });
29076             
29077             options.map = this.gMapContext.map;
29078             options.radius = radius;
29079             options.center = center;
29080             this.gMapContext.circle = new google.maps.Circle(options);
29081             return this.gMapContext.circle;
29082         }
29083         
29084         return null;
29085     },
29086     
29087     setPosition: function(location) 
29088     {
29089         this.gMapContext.location = location;
29090         this.gMapContext.marker.setPosition(location);
29091         this.gMapContext.map.panTo(location);
29092         this.drawCircle(location, this.gMapContext.radius, {});
29093         
29094         var _this = this;
29095         
29096         if (this.gMapContext.settings.enableReverseGeocode) {
29097             this.gMapContext.geodecoder.geocode({
29098                 latLng: this.gMapContext.location
29099             }, function(results, status) {
29100                 
29101                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29102                     _this.gMapContext.locationName = results[0].formatted_address;
29103                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29104                     
29105                     _this.fireEvent('positionchanged', this, location);
29106                 }
29107             });
29108             
29109             return;
29110         }
29111         
29112         this.fireEvent('positionchanged', this, location);
29113     },
29114     
29115     resize: function()
29116     {
29117         google.maps.event.trigger(this.gMapContext.map, "resize");
29118         
29119         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29120         
29121         this.fireEvent('resize', this);
29122     },
29123     
29124     setPositionByLatLng: function(latitude, longitude)
29125     {
29126         this.setPosition(new google.maps.LatLng(latitude, longitude));
29127     },
29128     
29129     getCurrentPosition: function() 
29130     {
29131         return {
29132             latitude: this.gMapContext.location.lat(),
29133             longitude: this.gMapContext.location.lng()
29134         };
29135     },
29136     
29137     getAddressName: function() 
29138     {
29139         return this.gMapContext.locationName;
29140     },
29141     
29142     getAddressComponents: function() 
29143     {
29144         return this.gMapContext.addressComponents;
29145     },
29146     
29147     address_component_from_google_geocode: function(address_components) 
29148     {
29149         var result = {};
29150         
29151         for (var i = 0; i < address_components.length; i++) {
29152             var component = address_components[i];
29153             if (component.types.indexOf("postal_code") >= 0) {
29154                 result.postalCode = component.short_name;
29155             } else if (component.types.indexOf("street_number") >= 0) {
29156                 result.streetNumber = component.short_name;
29157             } else if (component.types.indexOf("route") >= 0) {
29158                 result.streetName = component.short_name;
29159             } else if (component.types.indexOf("neighborhood") >= 0) {
29160                 result.city = component.short_name;
29161             } else if (component.types.indexOf("locality") >= 0) {
29162                 result.city = component.short_name;
29163             } else if (component.types.indexOf("sublocality") >= 0) {
29164                 result.district = component.short_name;
29165             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29166                 result.stateOrProvince = component.short_name;
29167             } else if (component.types.indexOf("country") >= 0) {
29168                 result.country = component.short_name;
29169             }
29170         }
29171         
29172         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29173         result.addressLine2 = "";
29174         return result;
29175     },
29176     
29177     setZoomLevel: function(zoom)
29178     {
29179         this.gMapContext.map.setZoom(zoom);
29180     },
29181     
29182     show: function()
29183     {
29184         if(!this.el){
29185             return;
29186         }
29187         
29188         this.el.show();
29189         
29190         this.resize();
29191         
29192         this.fireEvent('show', this);
29193     },
29194     
29195     hide: function()
29196     {
29197         if(!this.el){
29198             return;
29199         }
29200         
29201         this.el.hide();
29202         
29203         this.fireEvent('hide', this);
29204     }
29205     
29206 });
29207
29208 Roo.apply(Roo.bootstrap.LocationPicker, {
29209     
29210     OverlayView : function(map, options)
29211     {
29212         options = options || {};
29213         
29214         this.setMap(map);
29215     }
29216     
29217     
29218 });/**
29219  * @class Roo.bootstrap.Alert
29220  * @extends Roo.bootstrap.Component
29221  * Bootstrap Alert class - shows an alert area box
29222  * eg
29223  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29224   Enter a valid email address
29225 </div>
29226  * @licence LGPL
29227  * @cfg {String} title The title of alert
29228  * @cfg {String} html The content of alert
29229  * @cfg {String} weight (  success | info | warning | danger )
29230  * @cfg {String} faicon font-awesomeicon
29231  * 
29232  * @constructor
29233  * Create a new alert
29234  * @param {Object} config The config object
29235  */
29236
29237
29238 Roo.bootstrap.Alert = function(config){
29239     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29240     
29241 };
29242
29243 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29244     
29245     title: '',
29246     html: '',
29247     weight: false,
29248     faicon: false,
29249     
29250     getAutoCreate : function()
29251     {
29252         
29253         var cfg = {
29254             tag : 'div',
29255             cls : 'alert',
29256             cn : [
29257                 {
29258                     tag : 'i',
29259                     cls : 'roo-alert-icon'
29260                     
29261                 },
29262                 {
29263                     tag : 'b',
29264                     cls : 'roo-alert-title',
29265                     html : this.title
29266                 },
29267                 {
29268                     tag : 'span',
29269                     cls : 'roo-alert-text',
29270                     html : this.html
29271                 }
29272             ]
29273         };
29274         
29275         if(this.faicon){
29276             cfg.cn[0].cls += ' fa ' + this.faicon;
29277         }
29278         
29279         if(this.weight){
29280             cfg.cls += ' alert-' + this.weight;
29281         }
29282         
29283         return cfg;
29284     },
29285     
29286     initEvents: function() 
29287     {
29288         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29289     },
29290     
29291     setTitle : function(str)
29292     {
29293         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29294     },
29295     
29296     setText : function(str)
29297     {
29298         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29299     },
29300     
29301     setWeight : function(weight)
29302     {
29303         if(this.weight){
29304             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29305         }
29306         
29307         this.weight = weight;
29308         
29309         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29310     },
29311     
29312     setIcon : function(icon)
29313     {
29314         if(this.faicon){
29315             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29316         }
29317         
29318         this.faicon = icon;
29319         
29320         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29321     },
29322     
29323     hide: function() 
29324     {
29325         this.el.hide();   
29326     },
29327     
29328     show: function() 
29329     {  
29330         this.el.show();   
29331     }
29332     
29333 });
29334
29335  
29336 /*
29337 * Licence: LGPL
29338 */
29339
29340 /**
29341  * @class Roo.bootstrap.UploadCropbox
29342  * @extends Roo.bootstrap.Component
29343  * Bootstrap UploadCropbox class
29344  * @cfg {String} emptyText show when image has been loaded
29345  * @cfg {String} rotateNotify show when image too small to rotate
29346  * @cfg {Number} errorTimeout default 3000
29347  * @cfg {Number} minWidth default 300
29348  * @cfg {Number} minHeight default 300
29349  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29350  * @cfg {Boolean} isDocument (true|false) default false
29351  * @cfg {String} url action url
29352  * @cfg {String} paramName default 'imageUpload'
29353  * @cfg {String} method default POST
29354  * @cfg {Boolean} loadMask (true|false) default true
29355  * @cfg {Boolean} loadingText default 'Loading...'
29356  * 
29357  * @constructor
29358  * Create a new UploadCropbox
29359  * @param {Object} config The config object
29360  */
29361
29362 Roo.bootstrap.UploadCropbox = function(config){
29363     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29364     
29365     this.addEvents({
29366         /**
29367          * @event beforeselectfile
29368          * Fire before select file
29369          * @param {Roo.bootstrap.UploadCropbox} this
29370          */
29371         "beforeselectfile" : true,
29372         /**
29373          * @event initial
29374          * Fire after initEvent
29375          * @param {Roo.bootstrap.UploadCropbox} this
29376          */
29377         "initial" : true,
29378         /**
29379          * @event crop
29380          * Fire after initEvent
29381          * @param {Roo.bootstrap.UploadCropbox} this
29382          * @param {String} data
29383          */
29384         "crop" : true,
29385         /**
29386          * @event prepare
29387          * Fire when preparing the file data
29388          * @param {Roo.bootstrap.UploadCropbox} this
29389          * @param {Object} file
29390          */
29391         "prepare" : true,
29392         /**
29393          * @event exception
29394          * Fire when get exception
29395          * @param {Roo.bootstrap.UploadCropbox} this
29396          * @param {XMLHttpRequest} xhr
29397          */
29398         "exception" : true,
29399         /**
29400          * @event beforeloadcanvas
29401          * Fire before load the canvas
29402          * @param {Roo.bootstrap.UploadCropbox} this
29403          * @param {String} src
29404          */
29405         "beforeloadcanvas" : true,
29406         /**
29407          * @event trash
29408          * Fire when trash image
29409          * @param {Roo.bootstrap.UploadCropbox} this
29410          */
29411         "trash" : true,
29412         /**
29413          * @event download
29414          * Fire when download the image
29415          * @param {Roo.bootstrap.UploadCropbox} this
29416          */
29417         "download" : true,
29418         /**
29419          * @event footerbuttonclick
29420          * Fire when footerbuttonclick
29421          * @param {Roo.bootstrap.UploadCropbox} this
29422          * @param {String} type
29423          */
29424         "footerbuttonclick" : true,
29425         /**
29426          * @event resize
29427          * Fire when resize
29428          * @param {Roo.bootstrap.UploadCropbox} this
29429          */
29430         "resize" : true,
29431         /**
29432          * @event rotate
29433          * Fire when rotate the image
29434          * @param {Roo.bootstrap.UploadCropbox} this
29435          * @param {String} pos
29436          */
29437         "rotate" : true,
29438         /**
29439          * @event inspect
29440          * Fire when inspect the file
29441          * @param {Roo.bootstrap.UploadCropbox} this
29442          * @param {Object} file
29443          */
29444         "inspect" : true,
29445         /**
29446          * @event upload
29447          * Fire when xhr upload the file
29448          * @param {Roo.bootstrap.UploadCropbox} this
29449          * @param {Object} data
29450          */
29451         "upload" : true,
29452         /**
29453          * @event arrange
29454          * Fire when arrange the file data
29455          * @param {Roo.bootstrap.UploadCropbox} this
29456          * @param {Object} formData
29457          */
29458         "arrange" : true
29459     });
29460     
29461     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29462 };
29463
29464 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29465     
29466     emptyText : 'Click to upload image',
29467     rotateNotify : 'Image is too small to rotate',
29468     errorTimeout : 3000,
29469     scale : 0,
29470     baseScale : 1,
29471     rotate : 0,
29472     dragable : false,
29473     pinching : false,
29474     mouseX : 0,
29475     mouseY : 0,
29476     cropData : false,
29477     minWidth : 300,
29478     minHeight : 300,
29479     file : false,
29480     exif : {},
29481     baseRotate : 1,
29482     cropType : 'image/jpeg',
29483     buttons : false,
29484     canvasLoaded : false,
29485     isDocument : false,
29486     method : 'POST',
29487     paramName : 'imageUpload',
29488     loadMask : true,
29489     loadingText : 'Loading...',
29490     maskEl : false,
29491     
29492     getAutoCreate : function()
29493     {
29494         var cfg = {
29495             tag : 'div',
29496             cls : 'roo-upload-cropbox',
29497             cn : [
29498                 {
29499                     tag : 'input',
29500                     cls : 'roo-upload-cropbox-selector',
29501                     type : 'file'
29502                 },
29503                 {
29504                     tag : 'div',
29505                     cls : 'roo-upload-cropbox-body',
29506                     style : 'cursor:pointer',
29507                     cn : [
29508                         {
29509                             tag : 'div',
29510                             cls : 'roo-upload-cropbox-preview'
29511                         },
29512                         {
29513                             tag : 'div',
29514                             cls : 'roo-upload-cropbox-thumb'
29515                         },
29516                         {
29517                             tag : 'div',
29518                             cls : 'roo-upload-cropbox-empty-notify',
29519                             html : this.emptyText
29520                         },
29521                         {
29522                             tag : 'div',
29523                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29524                             html : this.rotateNotify
29525                         }
29526                     ]
29527                 },
29528                 {
29529                     tag : 'div',
29530                     cls : 'roo-upload-cropbox-footer',
29531                     cn : {
29532                         tag : 'div',
29533                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29534                         cn : []
29535                     }
29536                 }
29537             ]
29538         };
29539         
29540         return cfg;
29541     },
29542     
29543     onRender : function(ct, position)
29544     {
29545         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29546         
29547         if (this.buttons.length) {
29548             
29549             Roo.each(this.buttons, function(bb) {
29550                 
29551                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29552                 
29553                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29554                 
29555             }, this);
29556         }
29557         
29558         if(this.loadMask){
29559             this.maskEl = this.el;
29560         }
29561     },
29562     
29563     initEvents : function()
29564     {
29565         this.urlAPI = (window.createObjectURL && window) || 
29566                                 (window.URL && URL.revokeObjectURL && URL) || 
29567                                 (window.webkitURL && webkitURL);
29568                         
29569         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29570         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29571         
29572         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29573         this.selectorEl.hide();
29574         
29575         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29576         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29577         
29578         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29579         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29580         this.thumbEl.hide();
29581         
29582         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29583         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29584         
29585         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29586         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29587         this.errorEl.hide();
29588         
29589         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29590         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29591         this.footerEl.hide();
29592         
29593         this.setThumbBoxSize();
29594         
29595         this.bind();
29596         
29597         this.resize();
29598         
29599         this.fireEvent('initial', this);
29600     },
29601
29602     bind : function()
29603     {
29604         var _this = this;
29605         
29606         window.addEventListener("resize", function() { _this.resize(); } );
29607         
29608         this.bodyEl.on('click', this.beforeSelectFile, this);
29609         
29610         if(Roo.isTouch){
29611             this.bodyEl.on('touchstart', this.onTouchStart, this);
29612             this.bodyEl.on('touchmove', this.onTouchMove, this);
29613             this.bodyEl.on('touchend', this.onTouchEnd, this);
29614         }
29615         
29616         if(!Roo.isTouch){
29617             this.bodyEl.on('mousedown', this.onMouseDown, this);
29618             this.bodyEl.on('mousemove', this.onMouseMove, this);
29619             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29620             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29621             Roo.get(document).on('mouseup', this.onMouseUp, this);
29622         }
29623         
29624         this.selectorEl.on('change', this.onFileSelected, this);
29625     },
29626     
29627     reset : function()
29628     {    
29629         this.scale = 0;
29630         this.baseScale = 1;
29631         this.rotate = 0;
29632         this.baseRotate = 1;
29633         this.dragable = false;
29634         this.pinching = false;
29635         this.mouseX = 0;
29636         this.mouseY = 0;
29637         this.cropData = false;
29638         this.notifyEl.dom.innerHTML = this.emptyText;
29639         
29640         this.selectorEl.dom.value = '';
29641         
29642     },
29643     
29644     resize : function()
29645     {
29646         if(this.fireEvent('resize', this) != false){
29647             this.setThumbBoxPosition();
29648             this.setCanvasPosition();
29649         }
29650     },
29651     
29652     onFooterButtonClick : function(e, el, o, type)
29653     {
29654         switch (type) {
29655             case 'rotate-left' :
29656                 this.onRotateLeft(e);
29657                 break;
29658             case 'rotate-right' :
29659                 this.onRotateRight(e);
29660                 break;
29661             case 'picture' :
29662                 this.beforeSelectFile(e);
29663                 break;
29664             case 'trash' :
29665                 this.trash(e);
29666                 break;
29667             case 'crop' :
29668                 this.crop(e);
29669                 break;
29670             case 'download' :
29671                 this.download(e);
29672                 break;
29673             default :
29674                 break;
29675         }
29676         
29677         this.fireEvent('footerbuttonclick', this, type);
29678     },
29679     
29680     beforeSelectFile : function(e)
29681     {
29682         e.preventDefault();
29683         
29684         if(this.fireEvent('beforeselectfile', this) != false){
29685             this.selectorEl.dom.click();
29686         }
29687     },
29688     
29689     onFileSelected : function(e)
29690     {
29691         e.preventDefault();
29692         
29693         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29694             return;
29695         }
29696         
29697         var file = this.selectorEl.dom.files[0];
29698         
29699         if(this.fireEvent('inspect', this, file) != false){
29700             this.prepare(file);
29701         }
29702         
29703     },
29704     
29705     trash : function(e)
29706     {
29707         this.fireEvent('trash', this);
29708     },
29709     
29710     download : function(e)
29711     {
29712         this.fireEvent('download', this);
29713     },
29714     
29715     loadCanvas : function(src)
29716     {   
29717         if(this.fireEvent('beforeloadcanvas', this, src) != false){
29718             
29719             this.reset();
29720             
29721             this.imageEl = document.createElement('img');
29722             
29723             var _this = this;
29724             
29725             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29726             
29727             this.imageEl.src = src;
29728         }
29729     },
29730     
29731     onLoadCanvas : function()
29732     {   
29733         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29734         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29735         
29736         this.bodyEl.un('click', this.beforeSelectFile, this);
29737         
29738         this.notifyEl.hide();
29739         this.thumbEl.show();
29740         this.footerEl.show();
29741         
29742         this.baseRotateLevel();
29743         
29744         if(this.isDocument){
29745             this.setThumbBoxSize();
29746         }
29747         
29748         this.setThumbBoxPosition();
29749         
29750         this.baseScaleLevel();
29751         
29752         this.draw();
29753         
29754         this.resize();
29755         
29756         this.canvasLoaded = true;
29757         
29758         if(this.loadMask){
29759             this.maskEl.unmask();
29760         }
29761         
29762     },
29763     
29764     setCanvasPosition : function()
29765     {   
29766         if(!this.canvasEl){
29767             return;
29768         }
29769         
29770         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29771         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29772         
29773         this.previewEl.setLeft(pw);
29774         this.previewEl.setTop(ph);
29775         
29776     },
29777     
29778     onMouseDown : function(e)
29779     {   
29780         e.stopEvent();
29781         
29782         this.dragable = true;
29783         this.pinching = false;
29784         
29785         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29786             this.dragable = false;
29787             return;
29788         }
29789         
29790         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29791         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29792         
29793     },
29794     
29795     onMouseMove : function(e)
29796     {   
29797         e.stopEvent();
29798         
29799         if(!this.canvasLoaded){
29800             return;
29801         }
29802         
29803         if (!this.dragable){
29804             return;
29805         }
29806         
29807         var minX = Math.ceil(this.thumbEl.getLeft(true));
29808         var minY = Math.ceil(this.thumbEl.getTop(true));
29809         
29810         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
29811         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
29812         
29813         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29814         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29815         
29816         x = x - this.mouseX;
29817         y = y - this.mouseY;
29818         
29819         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
29820         var bgY = Math.ceil(y + this.previewEl.getTop(true));
29821         
29822         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
29823         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
29824         
29825         this.previewEl.setLeft(bgX);
29826         this.previewEl.setTop(bgY);
29827         
29828         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29829         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29830     },
29831     
29832     onMouseUp : function(e)
29833     {   
29834         e.stopEvent();
29835         
29836         this.dragable = false;
29837     },
29838     
29839     onMouseWheel : function(e)
29840     {   
29841         e.stopEvent();
29842         
29843         this.startScale = this.scale;
29844         
29845         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
29846         
29847         if(!this.zoomable()){
29848             this.scale = this.startScale;
29849             return;
29850         }
29851         
29852         this.draw();
29853         
29854         return;
29855     },
29856     
29857     zoomable : function()
29858     {
29859         var minScale = this.thumbEl.getWidth() / this.minWidth;
29860         
29861         if(this.minWidth < this.minHeight){
29862             minScale = this.thumbEl.getHeight() / this.minHeight;
29863         }
29864         
29865         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
29866         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
29867         
29868         if(
29869                 this.isDocument &&
29870                 (this.rotate == 0 || this.rotate == 180) && 
29871                 (
29872                     width > this.imageEl.OriginWidth || 
29873                     height > this.imageEl.OriginHeight ||
29874                     (width < this.minWidth && height < this.minHeight)
29875                 )
29876         ){
29877             return false;
29878         }
29879         
29880         if(
29881                 this.isDocument &&
29882                 (this.rotate == 90 || this.rotate == 270) && 
29883                 (
29884                     width > this.imageEl.OriginWidth || 
29885                     height > this.imageEl.OriginHeight ||
29886                     (width < this.minHeight && height < this.minWidth)
29887                 )
29888         ){
29889             return false;
29890         }
29891         
29892         if(
29893                 !this.isDocument &&
29894                 (this.rotate == 0 || this.rotate == 180) && 
29895                 (
29896                     width < this.minWidth || 
29897                     width > this.imageEl.OriginWidth || 
29898                     height < this.minHeight || 
29899                     height > this.imageEl.OriginHeight
29900                 )
29901         ){
29902             return false;
29903         }
29904         
29905         if(
29906                 !this.isDocument &&
29907                 (this.rotate == 90 || this.rotate == 270) && 
29908                 (
29909                     width < this.minHeight || 
29910                     width > this.imageEl.OriginWidth || 
29911                     height < this.minWidth || 
29912                     height > this.imageEl.OriginHeight
29913                 )
29914         ){
29915             return false;
29916         }
29917         
29918         return true;
29919         
29920     },
29921     
29922     onRotateLeft : function(e)
29923     {   
29924         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29925             
29926             var minScale = this.thumbEl.getWidth() / this.minWidth;
29927             
29928             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29929             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29930             
29931             this.startScale = this.scale;
29932             
29933             while (this.getScaleLevel() < minScale){
29934             
29935                 this.scale = this.scale + 1;
29936                 
29937                 if(!this.zoomable()){
29938                     break;
29939                 }
29940                 
29941                 if(
29942                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29943                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29944                 ){
29945                     continue;
29946                 }
29947                 
29948                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29949
29950                 this.draw();
29951                 
29952                 return;
29953             }
29954             
29955             this.scale = this.startScale;
29956             
29957             this.onRotateFail();
29958             
29959             return false;
29960         }
29961         
29962         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29963
29964         if(this.isDocument){
29965             this.setThumbBoxSize();
29966             this.setThumbBoxPosition();
29967             this.setCanvasPosition();
29968         }
29969         
29970         this.draw();
29971         
29972         this.fireEvent('rotate', this, 'left');
29973         
29974     },
29975     
29976     onRotateRight : function(e)
29977     {
29978         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29979             
29980             var minScale = this.thumbEl.getWidth() / this.minWidth;
29981         
29982             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29983             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29984             
29985             this.startScale = this.scale;
29986             
29987             while (this.getScaleLevel() < minScale){
29988             
29989                 this.scale = this.scale + 1;
29990                 
29991                 if(!this.zoomable()){
29992                     break;
29993                 }
29994                 
29995                 if(
29996                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29997                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29998                 ){
29999                     continue;
30000                 }
30001                 
30002                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30003
30004                 this.draw();
30005                 
30006                 return;
30007             }
30008             
30009             this.scale = this.startScale;
30010             
30011             this.onRotateFail();
30012             
30013             return false;
30014         }
30015         
30016         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30017
30018         if(this.isDocument){
30019             this.setThumbBoxSize();
30020             this.setThumbBoxPosition();
30021             this.setCanvasPosition();
30022         }
30023         
30024         this.draw();
30025         
30026         this.fireEvent('rotate', this, 'right');
30027     },
30028     
30029     onRotateFail : function()
30030     {
30031         this.errorEl.show(true);
30032         
30033         var _this = this;
30034         
30035         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30036     },
30037     
30038     draw : function()
30039     {
30040         this.previewEl.dom.innerHTML = '';
30041         
30042         var canvasEl = document.createElement("canvas");
30043         
30044         var contextEl = canvasEl.getContext("2d");
30045         
30046         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30047         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30048         var center = this.imageEl.OriginWidth / 2;
30049         
30050         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30051             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30052             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30053             center = this.imageEl.OriginHeight / 2;
30054         }
30055         
30056         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30057         
30058         contextEl.translate(center, center);
30059         contextEl.rotate(this.rotate * Math.PI / 180);
30060
30061         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30062         
30063         this.canvasEl = document.createElement("canvas");
30064         
30065         this.contextEl = this.canvasEl.getContext("2d");
30066         
30067         switch (this.rotate) {
30068             case 0 :
30069                 
30070                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30071                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30072                 
30073                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30074                 
30075                 break;
30076             case 90 : 
30077                 
30078                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30079                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30080                 
30081                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30082                     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);
30083                     break;
30084                 }
30085                 
30086                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30087                 
30088                 break;
30089             case 180 :
30090                 
30091                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30092                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30093                 
30094                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30095                     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);
30096                     break;
30097                 }
30098                 
30099                 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);
30100                 
30101                 break;
30102             case 270 :
30103                 
30104                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30105                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30106         
30107                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30108                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30109                     break;
30110                 }
30111                 
30112                 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);
30113                 
30114                 break;
30115             default : 
30116                 break;
30117         }
30118         
30119         this.previewEl.appendChild(this.canvasEl);
30120         
30121         this.setCanvasPosition();
30122     },
30123     
30124     crop : function()
30125     {
30126         if(!this.canvasLoaded){
30127             return;
30128         }
30129         
30130         var imageCanvas = document.createElement("canvas");
30131         
30132         var imageContext = imageCanvas.getContext("2d");
30133         
30134         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30135         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30136         
30137         var center = imageCanvas.width / 2;
30138         
30139         imageContext.translate(center, center);
30140         
30141         imageContext.rotate(this.rotate * Math.PI / 180);
30142         
30143         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30144         
30145         var canvas = document.createElement("canvas");
30146         
30147         var context = canvas.getContext("2d");
30148                 
30149         canvas.width = this.minWidth;
30150         canvas.height = this.minHeight;
30151
30152         switch (this.rotate) {
30153             case 0 :
30154                 
30155                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30156                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30157                 
30158                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30159                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30160                 
30161                 var targetWidth = this.minWidth - 2 * x;
30162                 var targetHeight = this.minHeight - 2 * y;
30163                 
30164                 var scale = 1;
30165                 
30166                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30167                     scale = targetWidth / width;
30168                 }
30169                 
30170                 if(x > 0 && y == 0){
30171                     scale = targetHeight / height;
30172                 }
30173                 
30174                 if(x > 0 && y > 0){
30175                     scale = targetWidth / width;
30176                     
30177                     if(width < height){
30178                         scale = targetHeight / height;
30179                     }
30180                 }
30181                 
30182                 context.scale(scale, scale);
30183                 
30184                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30185                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30186
30187                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30188                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30189
30190                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30191                 
30192                 break;
30193             case 90 : 
30194                 
30195                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30196                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30197                 
30198                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30199                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30200                 
30201                 var targetWidth = this.minWidth - 2 * x;
30202                 var targetHeight = this.minHeight - 2 * y;
30203                 
30204                 var scale = 1;
30205                 
30206                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30207                     scale = targetWidth / width;
30208                 }
30209                 
30210                 if(x > 0 && y == 0){
30211                     scale = targetHeight / height;
30212                 }
30213                 
30214                 if(x > 0 && y > 0){
30215                     scale = targetWidth / width;
30216                     
30217                     if(width < height){
30218                         scale = targetHeight / height;
30219                     }
30220                 }
30221                 
30222                 context.scale(scale, scale);
30223                 
30224                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30225                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30226
30227                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30228                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30229                 
30230                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30231                 
30232                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30233                 
30234                 break;
30235             case 180 :
30236                 
30237                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30238                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30239                 
30240                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30241                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30242                 
30243                 var targetWidth = this.minWidth - 2 * x;
30244                 var targetHeight = this.minHeight - 2 * y;
30245                 
30246                 var scale = 1;
30247                 
30248                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30249                     scale = targetWidth / width;
30250                 }
30251                 
30252                 if(x > 0 && y == 0){
30253                     scale = targetHeight / height;
30254                 }
30255                 
30256                 if(x > 0 && y > 0){
30257                     scale = targetWidth / width;
30258                     
30259                     if(width < height){
30260                         scale = targetHeight / height;
30261                     }
30262                 }
30263                 
30264                 context.scale(scale, scale);
30265                 
30266                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30267                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30268
30269                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30270                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30271
30272                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30273                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30274                 
30275                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30276                 
30277                 break;
30278             case 270 :
30279                 
30280                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30281                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30282                 
30283                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30284                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30285                 
30286                 var targetWidth = this.minWidth - 2 * x;
30287                 var targetHeight = this.minHeight - 2 * y;
30288                 
30289                 var scale = 1;
30290                 
30291                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30292                     scale = targetWidth / width;
30293                 }
30294                 
30295                 if(x > 0 && y == 0){
30296                     scale = targetHeight / height;
30297                 }
30298                 
30299                 if(x > 0 && y > 0){
30300                     scale = targetWidth / width;
30301                     
30302                     if(width < height){
30303                         scale = targetHeight / height;
30304                     }
30305                 }
30306                 
30307                 context.scale(scale, scale);
30308                 
30309                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30310                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30311
30312                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30313                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30314                 
30315                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30316                 
30317                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30318                 
30319                 break;
30320             default : 
30321                 break;
30322         }
30323         
30324         this.cropData = canvas.toDataURL(this.cropType);
30325         
30326         if(this.fireEvent('crop', this, this.cropData) !== false){
30327             this.process(this.file, this.cropData);
30328         }
30329         
30330         return;
30331         
30332     },
30333     
30334     setThumbBoxSize : function()
30335     {
30336         var width, height;
30337         
30338         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30339             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30340             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30341             
30342             this.minWidth = width;
30343             this.minHeight = height;
30344             
30345             if(this.rotate == 90 || this.rotate == 270){
30346                 this.minWidth = height;
30347                 this.minHeight = width;
30348             }
30349         }
30350         
30351         height = 300;
30352         width = Math.ceil(this.minWidth * height / this.minHeight);
30353         
30354         if(this.minWidth > this.minHeight){
30355             width = 300;
30356             height = Math.ceil(this.minHeight * width / this.minWidth);
30357         }
30358         
30359         this.thumbEl.setStyle({
30360             width : width + 'px',
30361             height : height + 'px'
30362         });
30363
30364         return;
30365             
30366     },
30367     
30368     setThumbBoxPosition : function()
30369     {
30370         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30371         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30372         
30373         this.thumbEl.setLeft(x);
30374         this.thumbEl.setTop(y);
30375         
30376     },
30377     
30378     baseRotateLevel : function()
30379     {
30380         this.baseRotate = 1;
30381         
30382         if(
30383                 typeof(this.exif) != 'undefined' &&
30384                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30385                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30386         ){
30387             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30388         }
30389         
30390         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30391         
30392     },
30393     
30394     baseScaleLevel : function()
30395     {
30396         var width, height;
30397         
30398         if(this.isDocument){
30399             
30400             if(this.baseRotate == 6 || this.baseRotate == 8){
30401             
30402                 height = this.thumbEl.getHeight();
30403                 this.baseScale = height / this.imageEl.OriginWidth;
30404
30405                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30406                     width = this.thumbEl.getWidth();
30407                     this.baseScale = width / this.imageEl.OriginHeight;
30408                 }
30409
30410                 return;
30411             }
30412
30413             height = this.thumbEl.getHeight();
30414             this.baseScale = height / this.imageEl.OriginHeight;
30415
30416             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30417                 width = this.thumbEl.getWidth();
30418                 this.baseScale = width / this.imageEl.OriginWidth;
30419             }
30420
30421             return;
30422         }
30423         
30424         if(this.baseRotate == 6 || this.baseRotate == 8){
30425             
30426             width = this.thumbEl.getHeight();
30427             this.baseScale = width / this.imageEl.OriginHeight;
30428             
30429             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30430                 height = this.thumbEl.getWidth();
30431                 this.baseScale = height / this.imageEl.OriginHeight;
30432             }
30433             
30434             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30435                 height = this.thumbEl.getWidth();
30436                 this.baseScale = height / this.imageEl.OriginHeight;
30437                 
30438                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30439                     width = this.thumbEl.getHeight();
30440                     this.baseScale = width / this.imageEl.OriginWidth;
30441                 }
30442             }
30443             
30444             return;
30445         }
30446         
30447         width = this.thumbEl.getWidth();
30448         this.baseScale = width / this.imageEl.OriginWidth;
30449         
30450         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30451             height = this.thumbEl.getHeight();
30452             this.baseScale = height / this.imageEl.OriginHeight;
30453         }
30454         
30455         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30456             
30457             height = this.thumbEl.getHeight();
30458             this.baseScale = height / this.imageEl.OriginHeight;
30459             
30460             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30461                 width = this.thumbEl.getWidth();
30462                 this.baseScale = width / this.imageEl.OriginWidth;
30463             }
30464             
30465         }
30466         
30467         return;
30468     },
30469     
30470     getScaleLevel : function()
30471     {
30472         return this.baseScale * Math.pow(1.1, this.scale);
30473     },
30474     
30475     onTouchStart : function(e)
30476     {
30477         if(!this.canvasLoaded){
30478             this.beforeSelectFile(e);
30479             return;
30480         }
30481         
30482         var touches = e.browserEvent.touches;
30483         
30484         if(!touches){
30485             return;
30486         }
30487         
30488         if(touches.length == 1){
30489             this.onMouseDown(e);
30490             return;
30491         }
30492         
30493         if(touches.length != 2){
30494             return;
30495         }
30496         
30497         var coords = [];
30498         
30499         for(var i = 0, finger; finger = touches[i]; i++){
30500             coords.push(finger.pageX, finger.pageY);
30501         }
30502         
30503         var x = Math.pow(coords[0] - coords[2], 2);
30504         var y = Math.pow(coords[1] - coords[3], 2);
30505         
30506         this.startDistance = Math.sqrt(x + y);
30507         
30508         this.startScale = this.scale;
30509         
30510         this.pinching = true;
30511         this.dragable = false;
30512         
30513     },
30514     
30515     onTouchMove : function(e)
30516     {
30517         if(!this.pinching && !this.dragable){
30518             return;
30519         }
30520         
30521         var touches = e.browserEvent.touches;
30522         
30523         if(!touches){
30524             return;
30525         }
30526         
30527         if(this.dragable){
30528             this.onMouseMove(e);
30529             return;
30530         }
30531         
30532         var coords = [];
30533         
30534         for(var i = 0, finger; finger = touches[i]; i++){
30535             coords.push(finger.pageX, finger.pageY);
30536         }
30537         
30538         var x = Math.pow(coords[0] - coords[2], 2);
30539         var y = Math.pow(coords[1] - coords[3], 2);
30540         
30541         this.endDistance = Math.sqrt(x + y);
30542         
30543         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30544         
30545         if(!this.zoomable()){
30546             this.scale = this.startScale;
30547             return;
30548         }
30549         
30550         this.draw();
30551         
30552     },
30553     
30554     onTouchEnd : function(e)
30555     {
30556         this.pinching = false;
30557         this.dragable = false;
30558         
30559     },
30560     
30561     process : function(file, crop)
30562     {
30563         if(this.loadMask){
30564             this.maskEl.mask(this.loadingText);
30565         }
30566         
30567         this.xhr = new XMLHttpRequest();
30568         
30569         file.xhr = this.xhr;
30570
30571         this.xhr.open(this.method, this.url, true);
30572         
30573         var headers = {
30574             "Accept": "application/json",
30575             "Cache-Control": "no-cache",
30576             "X-Requested-With": "XMLHttpRequest"
30577         };
30578         
30579         for (var headerName in headers) {
30580             var headerValue = headers[headerName];
30581             if (headerValue) {
30582                 this.xhr.setRequestHeader(headerName, headerValue);
30583             }
30584         }
30585         
30586         var _this = this;
30587         
30588         this.xhr.onload = function()
30589         {
30590             _this.xhrOnLoad(_this.xhr);
30591         }
30592         
30593         this.xhr.onerror = function()
30594         {
30595             _this.xhrOnError(_this.xhr);
30596         }
30597         
30598         var formData = new FormData();
30599
30600         formData.append('returnHTML', 'NO');
30601         
30602         if(crop){
30603             formData.append('crop', crop);
30604         }
30605         
30606         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30607             formData.append(this.paramName, file, file.name);
30608         }
30609         
30610         if(typeof(file.filename) != 'undefined'){
30611             formData.append('filename', file.filename);
30612         }
30613         
30614         if(typeof(file.mimetype) != 'undefined'){
30615             formData.append('mimetype', file.mimetype);
30616         }
30617         
30618         if(this.fireEvent('arrange', this, formData) != false){
30619             this.xhr.send(formData);
30620         };
30621     },
30622     
30623     xhrOnLoad : function(xhr)
30624     {
30625         if(this.loadMask){
30626             this.maskEl.unmask();
30627         }
30628         
30629         if (xhr.readyState !== 4) {
30630             this.fireEvent('exception', this, xhr);
30631             return;
30632         }
30633
30634         var response = Roo.decode(xhr.responseText);
30635         
30636         if(!response.success){
30637             this.fireEvent('exception', this, xhr);
30638             return;
30639         }
30640         
30641         var response = Roo.decode(xhr.responseText);
30642         
30643         this.fireEvent('upload', this, response);
30644         
30645     },
30646     
30647     xhrOnError : function()
30648     {
30649         if(this.loadMask){
30650             this.maskEl.unmask();
30651         }
30652         
30653         Roo.log('xhr on error');
30654         
30655         var response = Roo.decode(xhr.responseText);
30656           
30657         Roo.log(response);
30658         
30659     },
30660     
30661     prepare : function(file)
30662     {   
30663         if(this.loadMask){
30664             this.maskEl.mask(this.loadingText);
30665         }
30666         
30667         this.file = false;
30668         this.exif = {};
30669         
30670         if(typeof(file) === 'string'){
30671             this.loadCanvas(file);
30672             return;
30673         }
30674         
30675         if(!file || !this.urlAPI){
30676             return;
30677         }
30678         
30679         this.file = file;
30680         this.cropType = file.type;
30681         
30682         var _this = this;
30683         
30684         if(this.fireEvent('prepare', this, this.file) != false){
30685             
30686             var reader = new FileReader();
30687             
30688             reader.onload = function (e) {
30689                 if (e.target.error) {
30690                     Roo.log(e.target.error);
30691                     return;
30692                 }
30693                 
30694                 var buffer = e.target.result,
30695                     dataView = new DataView(buffer),
30696                     offset = 2,
30697                     maxOffset = dataView.byteLength - 4,
30698                     markerBytes,
30699                     markerLength;
30700                 
30701                 if (dataView.getUint16(0) === 0xffd8) {
30702                     while (offset < maxOffset) {
30703                         markerBytes = dataView.getUint16(offset);
30704                         
30705                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30706                             markerLength = dataView.getUint16(offset + 2) + 2;
30707                             if (offset + markerLength > dataView.byteLength) {
30708                                 Roo.log('Invalid meta data: Invalid segment size.');
30709                                 break;
30710                             }
30711                             
30712                             if(markerBytes == 0xffe1){
30713                                 _this.parseExifData(
30714                                     dataView,
30715                                     offset,
30716                                     markerLength
30717                                 );
30718                             }
30719                             
30720                             offset += markerLength;
30721                             
30722                             continue;
30723                         }
30724                         
30725                         break;
30726                     }
30727                     
30728                 }
30729                 
30730                 var url = _this.urlAPI.createObjectURL(_this.file);
30731                 
30732                 _this.loadCanvas(url);
30733                 
30734                 return;
30735             }
30736             
30737             reader.readAsArrayBuffer(this.file);
30738             
30739         }
30740         
30741     },
30742     
30743     parseExifData : function(dataView, offset, length)
30744     {
30745         var tiffOffset = offset + 10,
30746             littleEndian,
30747             dirOffset;
30748     
30749         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30750             // No Exif data, might be XMP data instead
30751             return;
30752         }
30753         
30754         // Check for the ASCII code for "Exif" (0x45786966):
30755         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30756             // No Exif data, might be XMP data instead
30757             return;
30758         }
30759         if (tiffOffset + 8 > dataView.byteLength) {
30760             Roo.log('Invalid Exif data: Invalid segment size.');
30761             return;
30762         }
30763         // Check for the two null bytes:
30764         if (dataView.getUint16(offset + 8) !== 0x0000) {
30765             Roo.log('Invalid Exif data: Missing byte alignment offset.');
30766             return;
30767         }
30768         // Check the byte alignment:
30769         switch (dataView.getUint16(tiffOffset)) {
30770         case 0x4949:
30771             littleEndian = true;
30772             break;
30773         case 0x4D4D:
30774             littleEndian = false;
30775             break;
30776         default:
30777             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30778             return;
30779         }
30780         // Check for the TIFF tag marker (0x002A):
30781         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30782             Roo.log('Invalid Exif data: Missing TIFF marker.');
30783             return;
30784         }
30785         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30786         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30787         
30788         this.parseExifTags(
30789             dataView,
30790             tiffOffset,
30791             tiffOffset + dirOffset,
30792             littleEndian
30793         );
30794     },
30795     
30796     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30797     {
30798         var tagsNumber,
30799             dirEndOffset,
30800             i;
30801         if (dirOffset + 6 > dataView.byteLength) {
30802             Roo.log('Invalid Exif data: Invalid directory offset.');
30803             return;
30804         }
30805         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
30806         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
30807         if (dirEndOffset + 4 > dataView.byteLength) {
30808             Roo.log('Invalid Exif data: Invalid directory size.');
30809             return;
30810         }
30811         for (i = 0; i < tagsNumber; i += 1) {
30812             this.parseExifTag(
30813                 dataView,
30814                 tiffOffset,
30815                 dirOffset + 2 + 12 * i, // tag offset
30816                 littleEndian
30817             );
30818         }
30819         // Return the offset to the next directory:
30820         return dataView.getUint32(dirEndOffset, littleEndian);
30821     },
30822     
30823     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
30824     {
30825         var tag = dataView.getUint16(offset, littleEndian);
30826         
30827         this.exif[tag] = this.getExifValue(
30828             dataView,
30829             tiffOffset,
30830             offset,
30831             dataView.getUint16(offset + 2, littleEndian), // tag type
30832             dataView.getUint32(offset + 4, littleEndian), // tag length
30833             littleEndian
30834         );
30835     },
30836     
30837     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
30838     {
30839         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
30840             tagSize,
30841             dataOffset,
30842             values,
30843             i,
30844             str,
30845             c;
30846     
30847         if (!tagType) {
30848             Roo.log('Invalid Exif data: Invalid tag type.');
30849             return;
30850         }
30851         
30852         tagSize = tagType.size * length;
30853         // Determine if the value is contained in the dataOffset bytes,
30854         // or if the value at the dataOffset is a pointer to the actual data:
30855         dataOffset = tagSize > 4 ?
30856                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
30857         if (dataOffset + tagSize > dataView.byteLength) {
30858             Roo.log('Invalid Exif data: Invalid data offset.');
30859             return;
30860         }
30861         if (length === 1) {
30862             return tagType.getValue(dataView, dataOffset, littleEndian);
30863         }
30864         values = [];
30865         for (i = 0; i < length; i += 1) {
30866             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
30867         }
30868         
30869         if (tagType.ascii) {
30870             str = '';
30871             // Concatenate the chars:
30872             for (i = 0; i < values.length; i += 1) {
30873                 c = values[i];
30874                 // Ignore the terminating NULL byte(s):
30875                 if (c === '\u0000') {
30876                     break;
30877                 }
30878                 str += c;
30879             }
30880             return str;
30881         }
30882         return values;
30883     }
30884     
30885 });
30886
30887 Roo.apply(Roo.bootstrap.UploadCropbox, {
30888     tags : {
30889         'Orientation': 0x0112
30890     },
30891     
30892     Orientation: {
30893             1: 0, //'top-left',
30894 //            2: 'top-right',
30895             3: 180, //'bottom-right',
30896 //            4: 'bottom-left',
30897 //            5: 'left-top',
30898             6: 90, //'right-top',
30899 //            7: 'right-bottom',
30900             8: 270 //'left-bottom'
30901     },
30902     
30903     exifTagTypes : {
30904         // byte, 8-bit unsigned int:
30905         1: {
30906             getValue: function (dataView, dataOffset) {
30907                 return dataView.getUint8(dataOffset);
30908             },
30909             size: 1
30910         },
30911         // ascii, 8-bit byte:
30912         2: {
30913             getValue: function (dataView, dataOffset) {
30914                 return String.fromCharCode(dataView.getUint8(dataOffset));
30915             },
30916             size: 1,
30917             ascii: true
30918         },
30919         // short, 16 bit int:
30920         3: {
30921             getValue: function (dataView, dataOffset, littleEndian) {
30922                 return dataView.getUint16(dataOffset, littleEndian);
30923             },
30924             size: 2
30925         },
30926         // long, 32 bit int:
30927         4: {
30928             getValue: function (dataView, dataOffset, littleEndian) {
30929                 return dataView.getUint32(dataOffset, littleEndian);
30930             },
30931             size: 4
30932         },
30933         // rational = two long values, first is numerator, second is denominator:
30934         5: {
30935             getValue: function (dataView, dataOffset, littleEndian) {
30936                 return dataView.getUint32(dataOffset, littleEndian) /
30937                     dataView.getUint32(dataOffset + 4, littleEndian);
30938             },
30939             size: 8
30940         },
30941         // slong, 32 bit signed int:
30942         9: {
30943             getValue: function (dataView, dataOffset, littleEndian) {
30944                 return dataView.getInt32(dataOffset, littleEndian);
30945             },
30946             size: 4
30947         },
30948         // srational, two slongs, first is numerator, second is denominator:
30949         10: {
30950             getValue: function (dataView, dataOffset, littleEndian) {
30951                 return dataView.getInt32(dataOffset, littleEndian) /
30952                     dataView.getInt32(dataOffset + 4, littleEndian);
30953             },
30954             size: 8
30955         }
30956     },
30957     
30958     footer : {
30959         STANDARD : [
30960             {
30961                 tag : 'div',
30962                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30963                 action : 'rotate-left',
30964                 cn : [
30965                     {
30966                         tag : 'button',
30967                         cls : 'btn btn-default',
30968                         html : '<i class="fa fa-undo"></i>'
30969                     }
30970                 ]
30971             },
30972             {
30973                 tag : 'div',
30974                 cls : 'btn-group roo-upload-cropbox-picture',
30975                 action : 'picture',
30976                 cn : [
30977                     {
30978                         tag : 'button',
30979                         cls : 'btn btn-default',
30980                         html : '<i class="fa fa-picture-o"></i>'
30981                     }
30982                 ]
30983             },
30984             {
30985                 tag : 'div',
30986                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30987                 action : 'rotate-right',
30988                 cn : [
30989                     {
30990                         tag : 'button',
30991                         cls : 'btn btn-default',
30992                         html : '<i class="fa fa-repeat"></i>'
30993                     }
30994                 ]
30995             }
30996         ],
30997         DOCUMENT : [
30998             {
30999                 tag : 'div',
31000                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31001                 action : 'rotate-left',
31002                 cn : [
31003                     {
31004                         tag : 'button',
31005                         cls : 'btn btn-default',
31006                         html : '<i class="fa fa-undo"></i>'
31007                     }
31008                 ]
31009             },
31010             {
31011                 tag : 'div',
31012                 cls : 'btn-group roo-upload-cropbox-download',
31013                 action : 'download',
31014                 cn : [
31015                     {
31016                         tag : 'button',
31017                         cls : 'btn btn-default',
31018                         html : '<i class="fa fa-download"></i>'
31019                     }
31020                 ]
31021             },
31022             {
31023                 tag : 'div',
31024                 cls : 'btn-group roo-upload-cropbox-crop',
31025                 action : 'crop',
31026                 cn : [
31027                     {
31028                         tag : 'button',
31029                         cls : 'btn btn-default',
31030                         html : '<i class="fa fa-crop"></i>'
31031                     }
31032                 ]
31033             },
31034             {
31035                 tag : 'div',
31036                 cls : 'btn-group roo-upload-cropbox-trash',
31037                 action : 'trash',
31038                 cn : [
31039                     {
31040                         tag : 'button',
31041                         cls : 'btn btn-default',
31042                         html : '<i class="fa fa-trash"></i>'
31043                     }
31044                 ]
31045             },
31046             {
31047                 tag : 'div',
31048                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31049                 action : 'rotate-right',
31050                 cn : [
31051                     {
31052                         tag : 'button',
31053                         cls : 'btn btn-default',
31054                         html : '<i class="fa fa-repeat"></i>'
31055                     }
31056                 ]
31057             }
31058         ],
31059         ROTATOR : [
31060             {
31061                 tag : 'div',
31062                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31063                 action : 'rotate-left',
31064                 cn : [
31065                     {
31066                         tag : 'button',
31067                         cls : 'btn btn-default',
31068                         html : '<i class="fa fa-undo"></i>'
31069                     }
31070                 ]
31071             },
31072             {
31073                 tag : 'div',
31074                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31075                 action : 'rotate-right',
31076                 cn : [
31077                     {
31078                         tag : 'button',
31079                         cls : 'btn btn-default',
31080                         html : '<i class="fa fa-repeat"></i>'
31081                     }
31082                 ]
31083             }
31084         ]
31085     }
31086 });
31087
31088 /*
31089 * Licence: LGPL
31090 */
31091
31092 /**
31093  * @class Roo.bootstrap.DocumentManager
31094  * @extends Roo.bootstrap.Component
31095  * Bootstrap DocumentManager class
31096  * @cfg {String} paramName default 'imageUpload'
31097  * @cfg {String} toolTipName default 'filename'
31098  * @cfg {String} method default POST
31099  * @cfg {String} url action url
31100  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31101  * @cfg {Boolean} multiple multiple upload default true
31102  * @cfg {Number} thumbSize default 300
31103  * @cfg {String} fieldLabel
31104  * @cfg {Number} labelWidth default 4
31105  * @cfg {String} labelAlign (left|top) default left
31106  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31107 * @cfg {Number} labellg set the width of label (1-12)
31108  * @cfg {Number} labelmd set the width of label (1-12)
31109  * @cfg {Number} labelsm set the width of label (1-12)
31110  * @cfg {Number} labelxs set the width of label (1-12)
31111  * 
31112  * @constructor
31113  * Create a new DocumentManager
31114  * @param {Object} config The config object
31115  */
31116
31117 Roo.bootstrap.DocumentManager = function(config){
31118     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31119     
31120     this.files = [];
31121     this.delegates = [];
31122     
31123     this.addEvents({
31124         /**
31125          * @event initial
31126          * Fire when initial the DocumentManager
31127          * @param {Roo.bootstrap.DocumentManager} this
31128          */
31129         "initial" : true,
31130         /**
31131          * @event inspect
31132          * inspect selected file
31133          * @param {Roo.bootstrap.DocumentManager} this
31134          * @param {File} file
31135          */
31136         "inspect" : true,
31137         /**
31138          * @event exception
31139          * Fire when xhr load exception
31140          * @param {Roo.bootstrap.DocumentManager} this
31141          * @param {XMLHttpRequest} xhr
31142          */
31143         "exception" : true,
31144         /**
31145          * @event afterupload
31146          * Fire when xhr load exception
31147          * @param {Roo.bootstrap.DocumentManager} this
31148          * @param {XMLHttpRequest} xhr
31149          */
31150         "afterupload" : true,
31151         /**
31152          * @event prepare
31153          * prepare the form data
31154          * @param {Roo.bootstrap.DocumentManager} this
31155          * @param {Object} formData
31156          */
31157         "prepare" : true,
31158         /**
31159          * @event remove
31160          * Fire when remove the file
31161          * @param {Roo.bootstrap.DocumentManager} this
31162          * @param {Object} file
31163          */
31164         "remove" : true,
31165         /**
31166          * @event refresh
31167          * Fire after refresh the file
31168          * @param {Roo.bootstrap.DocumentManager} this
31169          */
31170         "refresh" : true,
31171         /**
31172          * @event click
31173          * Fire after click the image
31174          * @param {Roo.bootstrap.DocumentManager} this
31175          * @param {Object} file
31176          */
31177         "click" : true,
31178         /**
31179          * @event edit
31180          * Fire when upload a image and editable set to true
31181          * @param {Roo.bootstrap.DocumentManager} this
31182          * @param {Object} file
31183          */
31184         "edit" : true,
31185         /**
31186          * @event beforeselectfile
31187          * Fire before select file
31188          * @param {Roo.bootstrap.DocumentManager} this
31189          */
31190         "beforeselectfile" : true,
31191         /**
31192          * @event process
31193          * Fire before process file
31194          * @param {Roo.bootstrap.DocumentManager} this
31195          * @param {Object} file
31196          */
31197         "process" : true,
31198         /**
31199          * @event previewrendered
31200          * Fire when preview rendered
31201          * @param {Roo.bootstrap.DocumentManager} this
31202          * @param {Object} file
31203          */
31204         "previewrendered" : true,
31205         /**
31206          */
31207         "previewResize" : true
31208         
31209     });
31210 };
31211
31212 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31213     
31214     boxes : 0,
31215     inputName : '',
31216     thumbSize : 300,
31217     multiple : true,
31218     files : false,
31219     method : 'POST',
31220     url : '',
31221     paramName : 'imageUpload',
31222     toolTipName : 'filename',
31223     fieldLabel : '',
31224     labelWidth : 4,
31225     labelAlign : 'left',
31226     editable : true,
31227     delegates : false,
31228     xhr : false, 
31229     
31230     labellg : 0,
31231     labelmd : 0,
31232     labelsm : 0,
31233     labelxs : 0,
31234     
31235     getAutoCreate : function()
31236     {   
31237         var managerWidget = {
31238             tag : 'div',
31239             cls : 'roo-document-manager',
31240             cn : [
31241                 {
31242                     tag : 'input',
31243                     cls : 'roo-document-manager-selector',
31244                     type : 'file'
31245                 },
31246                 {
31247                     tag : 'div',
31248                     cls : 'roo-document-manager-uploader',
31249                     cn : [
31250                         {
31251                             tag : 'div',
31252                             cls : 'roo-document-manager-upload-btn',
31253                             html : '<i class="fa fa-plus"></i>'
31254                         }
31255                     ]
31256                     
31257                 }
31258             ]
31259         };
31260         
31261         var content = [
31262             {
31263                 tag : 'div',
31264                 cls : 'column col-md-12',
31265                 cn : managerWidget
31266             }
31267         ];
31268         
31269         if(this.fieldLabel.length){
31270             
31271             content = [
31272                 {
31273                     tag : 'div',
31274                     cls : 'column col-md-12',
31275                     html : this.fieldLabel
31276                 },
31277                 {
31278                     tag : 'div',
31279                     cls : 'column col-md-12',
31280                     cn : managerWidget
31281                 }
31282             ];
31283
31284             if(this.labelAlign == 'left'){
31285                 content = [
31286                     {
31287                         tag : 'div',
31288                         cls : 'column',
31289                         html : this.fieldLabel
31290                     },
31291                     {
31292                         tag : 'div',
31293                         cls : 'column',
31294                         cn : managerWidget
31295                     }
31296                 ];
31297                 
31298                 if(this.labelWidth > 12){
31299                     content[0].style = "width: " + this.labelWidth + 'px';
31300                 }
31301
31302                 if(this.labelWidth < 13 && this.labelmd == 0){
31303                     this.labelmd = this.labelWidth;
31304                 }
31305
31306                 if(this.labellg > 0){
31307                     content[0].cls += ' col-lg-' + this.labellg;
31308                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31309                 }
31310
31311                 if(this.labelmd > 0){
31312                     content[0].cls += ' col-md-' + this.labelmd;
31313                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31314                 }
31315
31316                 if(this.labelsm > 0){
31317                     content[0].cls += ' col-sm-' + this.labelsm;
31318                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31319                 }
31320
31321                 if(this.labelxs > 0){
31322                     content[0].cls += ' col-xs-' + this.labelxs;
31323                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31324                 }
31325                 
31326             }
31327         }
31328         
31329         var cfg = {
31330             tag : 'div',
31331             cls : 'row clearfix',
31332             cn : content
31333         };
31334         
31335         return cfg;
31336         
31337     },
31338     
31339     initEvents : function()
31340     {
31341         this.managerEl = this.el.select('.roo-document-manager', true).first();
31342         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31343         
31344         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31345         this.selectorEl.hide();
31346         
31347         if(this.multiple){
31348             this.selectorEl.attr('multiple', 'multiple');
31349         }
31350         
31351         this.selectorEl.on('change', this.onFileSelected, this);
31352         
31353         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31354         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31355         
31356         this.uploader.on('click', this.onUploaderClick, this);
31357         
31358         this.renderProgressDialog();
31359         
31360         var _this = this;
31361         
31362         window.addEventListener("resize", function() { _this.refresh(); } );
31363         
31364         this.fireEvent('initial', this);
31365     },
31366     
31367     renderProgressDialog : function()
31368     {
31369         var _this = this;
31370         
31371         this.progressDialog = new Roo.bootstrap.Modal({
31372             cls : 'roo-document-manager-progress-dialog',
31373             allow_close : false,
31374             animate : false,
31375             title : '',
31376             buttons : [
31377                 {
31378                     name  :'cancel',
31379                     weight : 'danger',
31380                     html : 'Cancel'
31381                 }
31382             ], 
31383             listeners : { 
31384                 btnclick : function() {
31385                     _this.uploadCancel();
31386                     this.hide();
31387                 }
31388             }
31389         });
31390          
31391         this.progressDialog.render(Roo.get(document.body));
31392          
31393         this.progress = new Roo.bootstrap.Progress({
31394             cls : 'roo-document-manager-progress',
31395             active : true,
31396             striped : true
31397         });
31398         
31399         this.progress.render(this.progressDialog.getChildContainer());
31400         
31401         this.progressBar = new Roo.bootstrap.ProgressBar({
31402             cls : 'roo-document-manager-progress-bar',
31403             aria_valuenow : 0,
31404             aria_valuemin : 0,
31405             aria_valuemax : 12,
31406             panel : 'success'
31407         });
31408         
31409         this.progressBar.render(this.progress.getChildContainer());
31410     },
31411     
31412     onUploaderClick : function(e)
31413     {
31414         e.preventDefault();
31415      
31416         if(this.fireEvent('beforeselectfile', this) != false){
31417             this.selectorEl.dom.click();
31418         }
31419         
31420     },
31421     
31422     onFileSelected : function(e)
31423     {
31424         e.preventDefault();
31425         
31426         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31427             return;
31428         }
31429         
31430         Roo.each(this.selectorEl.dom.files, function(file){
31431             if(this.fireEvent('inspect', this, file) != false){
31432                 this.files.push(file);
31433             }
31434         }, this);
31435         
31436         this.queue();
31437         
31438     },
31439     
31440     queue : function()
31441     {
31442         this.selectorEl.dom.value = '';
31443         
31444         if(!this.files || !this.files.length){
31445             return;
31446         }
31447         
31448         if(this.boxes > 0 && this.files.length > this.boxes){
31449             this.files = this.files.slice(0, this.boxes);
31450         }
31451         
31452         this.uploader.show();
31453         
31454         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31455             this.uploader.hide();
31456         }
31457         
31458         var _this = this;
31459         
31460         var files = [];
31461         
31462         var docs = [];
31463         
31464         Roo.each(this.files, function(file){
31465             
31466             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31467                 var f = this.renderPreview(file);
31468                 files.push(f);
31469                 return;
31470             }
31471             
31472             if(file.type.indexOf('image') != -1){
31473                 this.delegates.push(
31474                     (function(){
31475                         _this.process(file);
31476                     }).createDelegate(this)
31477                 );
31478         
31479                 return;
31480             }
31481             
31482             docs.push(
31483                 (function(){
31484                     _this.process(file);
31485                 }).createDelegate(this)
31486             );
31487             
31488         }, this);
31489         
31490         this.files = files;
31491         
31492         this.delegates = this.delegates.concat(docs);
31493         
31494         if(!this.delegates.length){
31495             this.refresh();
31496             return;
31497         }
31498         
31499         this.progressBar.aria_valuemax = this.delegates.length;
31500         
31501         this.arrange();
31502         
31503         return;
31504     },
31505     
31506     arrange : function()
31507     {
31508         if(!this.delegates.length){
31509             this.progressDialog.hide();
31510             this.refresh();
31511             return;
31512         }
31513         
31514         var delegate = this.delegates.shift();
31515         
31516         this.progressDialog.show();
31517         
31518         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31519         
31520         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31521         
31522         delegate();
31523     },
31524     
31525     refresh : function()
31526     {
31527         this.uploader.show();
31528         
31529         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31530             this.uploader.hide();
31531         }
31532         
31533         Roo.isTouch ? this.closable(false) : this.closable(true);
31534         
31535         this.fireEvent('refresh', this);
31536     },
31537     
31538     onRemove : function(e, el, o)
31539     {
31540         e.preventDefault();
31541         
31542         this.fireEvent('remove', this, o);
31543         
31544     },
31545     
31546     remove : function(o)
31547     {
31548         var files = [];
31549         
31550         Roo.each(this.files, function(file){
31551             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31552                 files.push(file);
31553                 return;
31554             }
31555
31556             o.target.remove();
31557
31558         }, this);
31559         
31560         this.files = files;
31561         
31562         this.refresh();
31563     },
31564     
31565     clear : function()
31566     {
31567         Roo.each(this.files, function(file){
31568             if(!file.target){
31569                 return;
31570             }
31571             
31572             file.target.remove();
31573
31574         }, this);
31575         
31576         this.files = [];
31577         
31578         this.refresh();
31579     },
31580     
31581     onClick : function(e, el, o)
31582     {
31583         e.preventDefault();
31584         
31585         this.fireEvent('click', this, o);
31586         
31587     },
31588     
31589     closable : function(closable)
31590     {
31591         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31592             
31593             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31594             
31595             if(closable){
31596                 el.show();
31597                 return;
31598             }
31599             
31600             el.hide();
31601             
31602         }, this);
31603     },
31604     
31605     xhrOnLoad : function(xhr)
31606     {
31607         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31608             el.remove();
31609         }, this);
31610         
31611         if (xhr.readyState !== 4) {
31612             this.arrange();
31613             this.fireEvent('exception', this, xhr);
31614             return;
31615         }
31616
31617         var response = Roo.decode(xhr.responseText);
31618         
31619         if(!response.success){
31620             this.arrange();
31621             this.fireEvent('exception', this, xhr);
31622             return;
31623         }
31624         
31625         var file = this.renderPreview(response.data);
31626         
31627         this.files.push(file);
31628         
31629         this.arrange();
31630         
31631         this.fireEvent('afterupload', this, xhr);
31632         
31633     },
31634     
31635     xhrOnError : function(xhr)
31636     {
31637         Roo.log('xhr on error');
31638         
31639         var response = Roo.decode(xhr.responseText);
31640           
31641         Roo.log(response);
31642         
31643         this.arrange();
31644     },
31645     
31646     process : function(file)
31647     {
31648         if(this.fireEvent('process', this, file) !== false){
31649             if(this.editable && file.type.indexOf('image') != -1){
31650                 this.fireEvent('edit', this, file);
31651                 return;
31652             }
31653
31654             this.uploadStart(file, false);
31655
31656             return;
31657         }
31658         
31659     },
31660     
31661     uploadStart : function(file, crop)
31662     {
31663         this.xhr = new XMLHttpRequest();
31664         
31665         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31666             this.arrange();
31667             return;
31668         }
31669         
31670         file.xhr = this.xhr;
31671             
31672         this.managerEl.createChild({
31673             tag : 'div',
31674             cls : 'roo-document-manager-loading',
31675             cn : [
31676                 {
31677                     tag : 'div',
31678                     tooltip : file.name,
31679                     cls : 'roo-document-manager-thumb',
31680                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31681                 }
31682             ]
31683
31684         });
31685
31686         this.xhr.open(this.method, this.url, true);
31687         
31688         var headers = {
31689             "Accept": "application/json",
31690             "Cache-Control": "no-cache",
31691             "X-Requested-With": "XMLHttpRequest"
31692         };
31693         
31694         for (var headerName in headers) {
31695             var headerValue = headers[headerName];
31696             if (headerValue) {
31697                 this.xhr.setRequestHeader(headerName, headerValue);
31698             }
31699         }
31700         
31701         var _this = this;
31702         
31703         this.xhr.onload = function()
31704         {
31705             _this.xhrOnLoad(_this.xhr);
31706         }
31707         
31708         this.xhr.onerror = function()
31709         {
31710             _this.xhrOnError(_this.xhr);
31711         }
31712         
31713         var formData = new FormData();
31714
31715         formData.append('returnHTML', 'NO');
31716         
31717         if(crop){
31718             formData.append('crop', crop);
31719         }
31720         
31721         formData.append(this.paramName, file, file.name);
31722         
31723         var options = {
31724             file : file, 
31725             manually : false
31726         };
31727         
31728         if(this.fireEvent('prepare', this, formData, options) != false){
31729             
31730             if(options.manually){
31731                 return;
31732             }
31733             
31734             this.xhr.send(formData);
31735             return;
31736         };
31737         
31738         this.uploadCancel();
31739     },
31740     
31741     uploadCancel : function()
31742     {
31743         if (this.xhr) {
31744             this.xhr.abort();
31745         }
31746         
31747         this.delegates = [];
31748         
31749         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31750             el.remove();
31751         }, this);
31752         
31753         this.arrange();
31754     },
31755     
31756     renderPreview : function(file)
31757     {
31758         if(typeof(file.target) != 'undefined' && file.target){
31759             return file;
31760         }
31761         
31762         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31763         
31764         var previewEl = this.managerEl.createChild({
31765             tag : 'div',
31766             cls : 'roo-document-manager-preview',
31767             cn : [
31768                 {
31769                     tag : 'div',
31770                     tooltip : file[this.toolTipName],
31771                     cls : 'roo-document-manager-thumb',
31772                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31773                 },
31774                 {
31775                     tag : 'button',
31776                     cls : 'close',
31777                     html : '<i class="fa fa-times-circle"></i>'
31778                 }
31779             ]
31780         });
31781
31782         var close = previewEl.select('button.close', true).first();
31783
31784         close.on('click', this.onRemove, this, file);
31785
31786         file.target = previewEl;
31787
31788         var image = previewEl.select('img', true).first();
31789         
31790         var _this = this;
31791         
31792         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31793         
31794         image.on('click', this.onClick, this, file);
31795         
31796         this.fireEvent('previewrendered', this, file);
31797         
31798         return file;
31799         
31800     },
31801     
31802     onPreviewLoad : function(file, image)
31803     {
31804         if(typeof(file.target) == 'undefined' || !file.target){
31805             return;
31806         }
31807         
31808         var width = image.dom.naturalWidth || image.dom.width;
31809         var height = image.dom.naturalHeight || image.dom.height;
31810         
31811         if(!this.previewResize) {
31812             return;
31813         }
31814         
31815         if(width > height){
31816             file.target.addClass('wide');
31817             return;
31818         }
31819         
31820         file.target.addClass('tall');
31821         return;
31822         
31823     },
31824     
31825     uploadFromSource : function(file, crop)
31826     {
31827         this.xhr = new XMLHttpRequest();
31828         
31829         this.managerEl.createChild({
31830             tag : 'div',
31831             cls : 'roo-document-manager-loading',
31832             cn : [
31833                 {
31834                     tag : 'div',
31835                     tooltip : file.name,
31836                     cls : 'roo-document-manager-thumb',
31837                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31838                 }
31839             ]
31840
31841         });
31842
31843         this.xhr.open(this.method, this.url, true);
31844         
31845         var headers = {
31846             "Accept": "application/json",
31847             "Cache-Control": "no-cache",
31848             "X-Requested-With": "XMLHttpRequest"
31849         };
31850         
31851         for (var headerName in headers) {
31852             var headerValue = headers[headerName];
31853             if (headerValue) {
31854                 this.xhr.setRequestHeader(headerName, headerValue);
31855             }
31856         }
31857         
31858         var _this = this;
31859         
31860         this.xhr.onload = function()
31861         {
31862             _this.xhrOnLoad(_this.xhr);
31863         }
31864         
31865         this.xhr.onerror = function()
31866         {
31867             _this.xhrOnError(_this.xhr);
31868         }
31869         
31870         var formData = new FormData();
31871
31872         formData.append('returnHTML', 'NO');
31873         
31874         formData.append('crop', crop);
31875         
31876         if(typeof(file.filename) != 'undefined'){
31877             formData.append('filename', file.filename);
31878         }
31879         
31880         if(typeof(file.mimetype) != 'undefined'){
31881             formData.append('mimetype', file.mimetype);
31882         }
31883         
31884         Roo.log(formData);
31885         
31886         if(this.fireEvent('prepare', this, formData) != false){
31887             this.xhr.send(formData);
31888         };
31889     }
31890 });
31891
31892 /*
31893 * Licence: LGPL
31894 */
31895
31896 /**
31897  * @class Roo.bootstrap.DocumentViewer
31898  * @extends Roo.bootstrap.Component
31899  * Bootstrap DocumentViewer class
31900  * @cfg {Boolean} showDownload (true|false) show download button (default true)
31901  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
31902  * 
31903  * @constructor
31904  * Create a new DocumentViewer
31905  * @param {Object} config The config object
31906  */
31907
31908 Roo.bootstrap.DocumentViewer = function(config){
31909     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
31910     
31911     this.addEvents({
31912         /**
31913          * @event initial
31914          * Fire after initEvent
31915          * @param {Roo.bootstrap.DocumentViewer} this
31916          */
31917         "initial" : true,
31918         /**
31919          * @event click
31920          * Fire after click
31921          * @param {Roo.bootstrap.DocumentViewer} this
31922          */
31923         "click" : true,
31924         /**
31925          * @event download
31926          * Fire after download button
31927          * @param {Roo.bootstrap.DocumentViewer} this
31928          */
31929         "download" : true,
31930         /**
31931          * @event trash
31932          * Fire after trash button
31933          * @param {Roo.bootstrap.DocumentViewer} this
31934          */
31935         "trash" : true
31936         
31937     });
31938 };
31939
31940 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
31941     
31942     showDownload : true,
31943     
31944     showTrash : true,
31945     
31946     getAutoCreate : function()
31947     {
31948         var cfg = {
31949             tag : 'div',
31950             cls : 'roo-document-viewer',
31951             cn : [
31952                 {
31953                     tag : 'div',
31954                     cls : 'roo-document-viewer-body',
31955                     cn : [
31956                         {
31957                             tag : 'div',
31958                             cls : 'roo-document-viewer-thumb',
31959                             cn : [
31960                                 {
31961                                     tag : 'img',
31962                                     cls : 'roo-document-viewer-image'
31963                                 }
31964                             ]
31965                         }
31966                     ]
31967                 },
31968                 {
31969                     tag : 'div',
31970                     cls : 'roo-document-viewer-footer',
31971                     cn : {
31972                         tag : 'div',
31973                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
31974                         cn : [
31975                             {
31976                                 tag : 'div',
31977                                 cls : 'btn-group roo-document-viewer-download',
31978                                 cn : [
31979                                     {
31980                                         tag : 'button',
31981                                         cls : 'btn btn-default',
31982                                         html : '<i class="fa fa-download"></i>'
31983                                     }
31984                                 ]
31985                             },
31986                             {
31987                                 tag : 'div',
31988                                 cls : 'btn-group roo-document-viewer-trash',
31989                                 cn : [
31990                                     {
31991                                         tag : 'button',
31992                                         cls : 'btn btn-default',
31993                                         html : '<i class="fa fa-trash"></i>'
31994                                     }
31995                                 ]
31996                             }
31997                         ]
31998                     }
31999                 }
32000             ]
32001         };
32002         
32003         return cfg;
32004     },
32005     
32006     initEvents : function()
32007     {
32008         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32009         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32010         
32011         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32012         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32013         
32014         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32015         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32016         
32017         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32018         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32019         
32020         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32021         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32022         
32023         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32024         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32025         
32026         this.bodyEl.on('click', this.onClick, this);
32027         this.downloadBtn.on('click', this.onDownload, this);
32028         this.trashBtn.on('click', this.onTrash, this);
32029         
32030         this.downloadBtn.hide();
32031         this.trashBtn.hide();
32032         
32033         if(this.showDownload){
32034             this.downloadBtn.show();
32035         }
32036         
32037         if(this.showTrash){
32038             this.trashBtn.show();
32039         }
32040         
32041         if(!this.showDownload && !this.showTrash) {
32042             this.footerEl.hide();
32043         }
32044         
32045     },
32046     
32047     initial : function()
32048     {
32049         this.fireEvent('initial', this);
32050         
32051     },
32052     
32053     onClick : function(e)
32054     {
32055         e.preventDefault();
32056         
32057         this.fireEvent('click', this);
32058     },
32059     
32060     onDownload : function(e)
32061     {
32062         e.preventDefault();
32063         
32064         this.fireEvent('download', this);
32065     },
32066     
32067     onTrash : function(e)
32068     {
32069         e.preventDefault();
32070         
32071         this.fireEvent('trash', this);
32072     }
32073     
32074 });
32075 /*
32076  * - LGPL
32077  *
32078  * nav progress bar
32079  * 
32080  */
32081
32082 /**
32083  * @class Roo.bootstrap.NavProgressBar
32084  * @extends Roo.bootstrap.Component
32085  * Bootstrap NavProgressBar class
32086  * 
32087  * @constructor
32088  * Create a new nav progress bar
32089  * @param {Object} config The config object
32090  */
32091
32092 Roo.bootstrap.NavProgressBar = function(config){
32093     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32094
32095     this.bullets = this.bullets || [];
32096    
32097 //    Roo.bootstrap.NavProgressBar.register(this);
32098      this.addEvents({
32099         /**
32100              * @event changed
32101              * Fires when the active item changes
32102              * @param {Roo.bootstrap.NavProgressBar} this
32103              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32104              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32105          */
32106         'changed': true
32107      });
32108     
32109 };
32110
32111 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32112     
32113     bullets : [],
32114     barItems : [],
32115     
32116     getAutoCreate : function()
32117     {
32118         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32119         
32120         cfg = {
32121             tag : 'div',
32122             cls : 'roo-navigation-bar-group',
32123             cn : [
32124                 {
32125                     tag : 'div',
32126                     cls : 'roo-navigation-top-bar'
32127                 },
32128                 {
32129                     tag : 'div',
32130                     cls : 'roo-navigation-bullets-bar',
32131                     cn : [
32132                         {
32133                             tag : 'ul',
32134                             cls : 'roo-navigation-bar'
32135                         }
32136                     ]
32137                 },
32138                 
32139                 {
32140                     tag : 'div',
32141                     cls : 'roo-navigation-bottom-bar'
32142                 }
32143             ]
32144             
32145         };
32146         
32147         return cfg;
32148         
32149     },
32150     
32151     initEvents: function() 
32152     {
32153         
32154     },
32155     
32156     onRender : function(ct, position) 
32157     {
32158         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32159         
32160         if(this.bullets.length){
32161             Roo.each(this.bullets, function(b){
32162                this.addItem(b);
32163             }, this);
32164         }
32165         
32166         this.format();
32167         
32168     },
32169     
32170     addItem : function(cfg)
32171     {
32172         var item = new Roo.bootstrap.NavProgressItem(cfg);
32173         
32174         item.parentId = this.id;
32175         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32176         
32177         if(cfg.html){
32178             var top = new Roo.bootstrap.Element({
32179                 tag : 'div',
32180                 cls : 'roo-navigation-bar-text'
32181             });
32182             
32183             var bottom = new Roo.bootstrap.Element({
32184                 tag : 'div',
32185                 cls : 'roo-navigation-bar-text'
32186             });
32187             
32188             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32189             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32190             
32191             var topText = new Roo.bootstrap.Element({
32192                 tag : 'span',
32193                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32194             });
32195             
32196             var bottomText = new Roo.bootstrap.Element({
32197                 tag : 'span',
32198                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32199             });
32200             
32201             topText.onRender(top.el, null);
32202             bottomText.onRender(bottom.el, null);
32203             
32204             item.topEl = top;
32205             item.bottomEl = bottom;
32206         }
32207         
32208         this.barItems.push(item);
32209         
32210         return item;
32211     },
32212     
32213     getActive : function()
32214     {
32215         var active = false;
32216         
32217         Roo.each(this.barItems, function(v){
32218             
32219             if (!v.isActive()) {
32220                 return;
32221             }
32222             
32223             active = v;
32224             return false;
32225             
32226         });
32227         
32228         return active;
32229     },
32230     
32231     setActiveItem : function(item)
32232     {
32233         var prev = false;
32234         
32235         Roo.each(this.barItems, function(v){
32236             if (v.rid == item.rid) {
32237                 return ;
32238             }
32239             
32240             if (v.isActive()) {
32241                 v.setActive(false);
32242                 prev = v;
32243             }
32244         });
32245
32246         item.setActive(true);
32247         
32248         this.fireEvent('changed', this, item, prev);
32249     },
32250     
32251     getBarItem: function(rid)
32252     {
32253         var ret = false;
32254         
32255         Roo.each(this.barItems, function(e) {
32256             if (e.rid != rid) {
32257                 return;
32258             }
32259             
32260             ret =  e;
32261             return false;
32262         });
32263         
32264         return ret;
32265     },
32266     
32267     indexOfItem : function(item)
32268     {
32269         var index = false;
32270         
32271         Roo.each(this.barItems, function(v, i){
32272             
32273             if (v.rid != item.rid) {
32274                 return;
32275             }
32276             
32277             index = i;
32278             return false
32279         });
32280         
32281         return index;
32282     },
32283     
32284     setActiveNext : function()
32285     {
32286         var i = this.indexOfItem(this.getActive());
32287         
32288         if (i > this.barItems.length) {
32289             return;
32290         }
32291         
32292         this.setActiveItem(this.barItems[i+1]);
32293     },
32294     
32295     setActivePrev : function()
32296     {
32297         var i = this.indexOfItem(this.getActive());
32298         
32299         if (i  < 1) {
32300             return;
32301         }
32302         
32303         this.setActiveItem(this.barItems[i-1]);
32304     },
32305     
32306     format : function()
32307     {
32308         if(!this.barItems.length){
32309             return;
32310         }
32311      
32312         var width = 100 / this.barItems.length;
32313         
32314         Roo.each(this.barItems, function(i){
32315             i.el.setStyle('width', width + '%');
32316             i.topEl.el.setStyle('width', width + '%');
32317             i.bottomEl.el.setStyle('width', width + '%');
32318         }, this);
32319         
32320     }
32321     
32322 });
32323 /*
32324  * - LGPL
32325  *
32326  * Nav Progress Item
32327  * 
32328  */
32329
32330 /**
32331  * @class Roo.bootstrap.NavProgressItem
32332  * @extends Roo.bootstrap.Component
32333  * Bootstrap NavProgressItem class
32334  * @cfg {String} rid the reference id
32335  * @cfg {Boolean} active (true|false) Is item active default false
32336  * @cfg {Boolean} disabled (true|false) Is item active default false
32337  * @cfg {String} html
32338  * @cfg {String} position (top|bottom) text position default bottom
32339  * @cfg {String} icon show icon instead of number
32340  * 
32341  * @constructor
32342  * Create a new NavProgressItem
32343  * @param {Object} config The config object
32344  */
32345 Roo.bootstrap.NavProgressItem = function(config){
32346     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32347     this.addEvents({
32348         // raw events
32349         /**
32350          * @event click
32351          * The raw click event for the entire grid.
32352          * @param {Roo.bootstrap.NavProgressItem} this
32353          * @param {Roo.EventObject} e
32354          */
32355         "click" : true
32356     });
32357    
32358 };
32359
32360 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32361     
32362     rid : '',
32363     active : false,
32364     disabled : false,
32365     html : '',
32366     position : 'bottom',
32367     icon : false,
32368     
32369     getAutoCreate : function()
32370     {
32371         var iconCls = 'roo-navigation-bar-item-icon';
32372         
32373         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32374         
32375         var cfg = {
32376             tag: 'li',
32377             cls: 'roo-navigation-bar-item',
32378             cn : [
32379                 {
32380                     tag : 'i',
32381                     cls : iconCls
32382                 }
32383             ]
32384         };
32385         
32386         if(this.active){
32387             cfg.cls += ' active';
32388         }
32389         if(this.disabled){
32390             cfg.cls += ' disabled';
32391         }
32392         
32393         return cfg;
32394     },
32395     
32396     disable : function()
32397     {
32398         this.setDisabled(true);
32399     },
32400     
32401     enable : function()
32402     {
32403         this.setDisabled(false);
32404     },
32405     
32406     initEvents: function() 
32407     {
32408         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32409         
32410         this.iconEl.on('click', this.onClick, this);
32411     },
32412     
32413     onClick : function(e)
32414     {
32415         e.preventDefault();
32416         
32417         if(this.disabled){
32418             return;
32419         }
32420         
32421         if(this.fireEvent('click', this, e) === false){
32422             return;
32423         };
32424         
32425         this.parent().setActiveItem(this);
32426     },
32427     
32428     isActive: function () 
32429     {
32430         return this.active;
32431     },
32432     
32433     setActive : function(state)
32434     {
32435         if(this.active == state){
32436             return;
32437         }
32438         
32439         this.active = state;
32440         
32441         if (state) {
32442             this.el.addClass('active');
32443             return;
32444         }
32445         
32446         this.el.removeClass('active');
32447         
32448         return;
32449     },
32450     
32451     setDisabled : function(state)
32452     {
32453         if(this.disabled == state){
32454             return;
32455         }
32456         
32457         this.disabled = state;
32458         
32459         if (state) {
32460             this.el.addClass('disabled');
32461             return;
32462         }
32463         
32464         this.el.removeClass('disabled');
32465     },
32466     
32467     tooltipEl : function()
32468     {
32469         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32470     }
32471 });
32472  
32473
32474  /*
32475  * - LGPL
32476  *
32477  * FieldLabel
32478  * 
32479  */
32480
32481 /**
32482  * @class Roo.bootstrap.FieldLabel
32483  * @extends Roo.bootstrap.Component
32484  * Bootstrap FieldLabel class
32485  * @cfg {String} html contents of the element
32486  * @cfg {String} tag tag of the element default label
32487  * @cfg {String} cls class of the element
32488  * @cfg {String} target label target 
32489  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32490  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32491  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32492  * @cfg {String} iconTooltip default "This field is required"
32493  * @cfg {String} indicatorpos (left|right) default left
32494  * 
32495  * @constructor
32496  * Create a new FieldLabel
32497  * @param {Object} config The config object
32498  */
32499
32500 Roo.bootstrap.FieldLabel = function(config){
32501     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32502     
32503     this.addEvents({
32504             /**
32505              * @event invalid
32506              * Fires after the field has been marked as invalid.
32507              * @param {Roo.form.FieldLabel} this
32508              * @param {String} msg The validation message
32509              */
32510             invalid : true,
32511             /**
32512              * @event valid
32513              * Fires after the field has been validated with no errors.
32514              * @param {Roo.form.FieldLabel} this
32515              */
32516             valid : true
32517         });
32518 };
32519
32520 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32521     
32522     tag: 'label',
32523     cls: '',
32524     html: '',
32525     target: '',
32526     allowBlank : true,
32527     invalidClass : 'has-warning',
32528     validClass : 'has-success',
32529     iconTooltip : 'This field is required',
32530     indicatorpos : 'left',
32531     
32532     getAutoCreate : function(){
32533         
32534         var cls = "";
32535         if (!this.allowBlank) {
32536             cls  = "visible";
32537         }
32538         
32539         var cfg = {
32540             tag : this.tag,
32541             cls : 'roo-bootstrap-field-label ' + this.cls,
32542             for : this.target,
32543             cn : [
32544                 {
32545                     tag : 'i',
32546                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32547                     tooltip : this.iconTooltip
32548                 },
32549                 {
32550                     tag : 'span',
32551                     html : this.html
32552                 }
32553             ] 
32554         };
32555         
32556         if(this.indicatorpos == 'right'){
32557             var cfg = {
32558                 tag : this.tag,
32559                 cls : 'roo-bootstrap-field-label ' + this.cls,
32560                 for : this.target,
32561                 cn : [
32562                     {
32563                         tag : 'span',
32564                         html : this.html
32565                     },
32566                     {
32567                         tag : 'i',
32568                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32569                         tooltip : this.iconTooltip
32570                     }
32571                 ] 
32572             };
32573         }
32574         
32575         return cfg;
32576     },
32577     
32578     initEvents: function() 
32579     {
32580         Roo.bootstrap.Element.superclass.initEvents.call(this);
32581         
32582         this.indicator = this.indicatorEl();
32583         
32584         if(this.indicator){
32585             this.indicator.removeClass('visible');
32586             this.indicator.addClass('invisible');
32587         }
32588         
32589         Roo.bootstrap.FieldLabel.register(this);
32590     },
32591     
32592     indicatorEl : function()
32593     {
32594         var indicator = this.el.select('i.roo-required-indicator',true).first();
32595         
32596         if(!indicator){
32597             return false;
32598         }
32599         
32600         return indicator;
32601         
32602     },
32603     
32604     /**
32605      * Mark this field as valid
32606      */
32607     markValid : function()
32608     {
32609         if(this.indicator){
32610             this.indicator.removeClass('visible');
32611             this.indicator.addClass('invisible');
32612         }
32613         if (Roo.bootstrap.version == 3) {
32614             this.el.removeClass(this.invalidClass);
32615             this.el.addClass(this.validClass);
32616         } else {
32617             this.el.removeClass('is-invalid');
32618             this.el.addClass('is-valid');
32619         }
32620         
32621         
32622         this.fireEvent('valid', this);
32623     },
32624     
32625     /**
32626      * Mark this field as invalid
32627      * @param {String} msg The validation message
32628      */
32629     markInvalid : function(msg)
32630     {
32631         if(this.indicator){
32632             this.indicator.removeClass('invisible');
32633             this.indicator.addClass('visible');
32634         }
32635           if (Roo.bootstrap.version == 3) {
32636             this.el.removeClass(this.validClass);
32637             this.el.addClass(this.invalidClass);
32638         } else {
32639             this.el.removeClass('is-valid');
32640             this.el.addClass('is-invalid');
32641         }
32642         
32643         
32644         this.fireEvent('invalid', this, msg);
32645     }
32646     
32647    
32648 });
32649
32650 Roo.apply(Roo.bootstrap.FieldLabel, {
32651     
32652     groups: {},
32653     
32654      /**
32655     * register a FieldLabel Group
32656     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32657     */
32658     register : function(label)
32659     {
32660         if(this.groups.hasOwnProperty(label.target)){
32661             return;
32662         }
32663      
32664         this.groups[label.target] = label;
32665         
32666     },
32667     /**
32668     * fetch a FieldLabel Group based on the target
32669     * @param {string} target
32670     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32671     */
32672     get: function(target) {
32673         if (typeof(this.groups[target]) == 'undefined') {
32674             return false;
32675         }
32676         
32677         return this.groups[target] ;
32678     }
32679 });
32680
32681  
32682
32683  /*
32684  * - LGPL
32685  *
32686  * page DateSplitField.
32687  * 
32688  */
32689
32690
32691 /**
32692  * @class Roo.bootstrap.DateSplitField
32693  * @extends Roo.bootstrap.Component
32694  * Bootstrap DateSplitField class
32695  * @cfg {string} fieldLabel - the label associated
32696  * @cfg {Number} labelWidth set the width of label (0-12)
32697  * @cfg {String} labelAlign (top|left)
32698  * @cfg {Boolean} dayAllowBlank (true|false) default false
32699  * @cfg {Boolean} monthAllowBlank (true|false) default false
32700  * @cfg {Boolean} yearAllowBlank (true|false) default false
32701  * @cfg {string} dayPlaceholder 
32702  * @cfg {string} monthPlaceholder
32703  * @cfg {string} yearPlaceholder
32704  * @cfg {string} dayFormat default 'd'
32705  * @cfg {string} monthFormat default 'm'
32706  * @cfg {string} yearFormat default 'Y'
32707  * @cfg {Number} labellg set the width of label (1-12)
32708  * @cfg {Number} labelmd set the width of label (1-12)
32709  * @cfg {Number} labelsm set the width of label (1-12)
32710  * @cfg {Number} labelxs set the width of label (1-12)
32711
32712  *     
32713  * @constructor
32714  * Create a new DateSplitField
32715  * @param {Object} config The config object
32716  */
32717
32718 Roo.bootstrap.DateSplitField = function(config){
32719     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32720     
32721     this.addEvents({
32722         // raw events
32723          /**
32724          * @event years
32725          * getting the data of years
32726          * @param {Roo.bootstrap.DateSplitField} this
32727          * @param {Object} years
32728          */
32729         "years" : true,
32730         /**
32731          * @event days
32732          * getting the data of days
32733          * @param {Roo.bootstrap.DateSplitField} this
32734          * @param {Object} days
32735          */
32736         "days" : true,
32737         /**
32738          * @event invalid
32739          * Fires after the field has been marked as invalid.
32740          * @param {Roo.form.Field} this
32741          * @param {String} msg The validation message
32742          */
32743         invalid : true,
32744        /**
32745          * @event valid
32746          * Fires after the field has been validated with no errors.
32747          * @param {Roo.form.Field} this
32748          */
32749         valid : true
32750     });
32751 };
32752
32753 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
32754     
32755     fieldLabel : '',
32756     labelAlign : 'top',
32757     labelWidth : 3,
32758     dayAllowBlank : false,
32759     monthAllowBlank : false,
32760     yearAllowBlank : false,
32761     dayPlaceholder : '',
32762     monthPlaceholder : '',
32763     yearPlaceholder : '',
32764     dayFormat : 'd',
32765     monthFormat : 'm',
32766     yearFormat : 'Y',
32767     isFormField : true,
32768     labellg : 0,
32769     labelmd : 0,
32770     labelsm : 0,
32771     labelxs : 0,
32772     
32773     getAutoCreate : function()
32774     {
32775         var cfg = {
32776             tag : 'div',
32777             cls : 'row roo-date-split-field-group',
32778             cn : [
32779                 {
32780                     tag : 'input',
32781                     type : 'hidden',
32782                     cls : 'form-hidden-field roo-date-split-field-group-value',
32783                     name : this.name
32784                 }
32785             ]
32786         };
32787         
32788         var labelCls = 'col-md-12';
32789         var contentCls = 'col-md-4';
32790         
32791         if(this.fieldLabel){
32792             
32793             var label = {
32794                 tag : 'div',
32795                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32796                 cn : [
32797                     {
32798                         tag : 'label',
32799                         html : this.fieldLabel
32800                     }
32801                 ]
32802             };
32803             
32804             if(this.labelAlign == 'left'){
32805             
32806                 if(this.labelWidth > 12){
32807                     label.style = "width: " + this.labelWidth + 'px';
32808                 }
32809
32810                 if(this.labelWidth < 13 && this.labelmd == 0){
32811                     this.labelmd = this.labelWidth;
32812                 }
32813
32814                 if(this.labellg > 0){
32815                     labelCls = ' col-lg-' + this.labellg;
32816                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
32817                 }
32818
32819                 if(this.labelmd > 0){
32820                     labelCls = ' col-md-' + this.labelmd;
32821                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
32822                 }
32823
32824                 if(this.labelsm > 0){
32825                     labelCls = ' col-sm-' + this.labelsm;
32826                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
32827                 }
32828
32829                 if(this.labelxs > 0){
32830                     labelCls = ' col-xs-' + this.labelxs;
32831                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
32832                 }
32833             }
32834             
32835             label.cls += ' ' + labelCls;
32836             
32837             cfg.cn.push(label);
32838         }
32839         
32840         Roo.each(['day', 'month', 'year'], function(t){
32841             cfg.cn.push({
32842                 tag : 'div',
32843                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
32844             });
32845         }, this);
32846         
32847         return cfg;
32848     },
32849     
32850     inputEl: function ()
32851     {
32852         return this.el.select('.roo-date-split-field-group-value', true).first();
32853     },
32854     
32855     onRender : function(ct, position) 
32856     {
32857         var _this = this;
32858         
32859         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32860         
32861         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
32862         
32863         this.dayField = new Roo.bootstrap.ComboBox({
32864             allowBlank : this.dayAllowBlank,
32865             alwaysQuery : true,
32866             displayField : 'value',
32867             editable : false,
32868             fieldLabel : '',
32869             forceSelection : true,
32870             mode : 'local',
32871             placeholder : this.dayPlaceholder,
32872             selectOnFocus : true,
32873             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32874             triggerAction : 'all',
32875             typeAhead : true,
32876             valueField : 'value',
32877             store : new Roo.data.SimpleStore({
32878                 data : (function() {    
32879                     var days = [];
32880                     _this.fireEvent('days', _this, days);
32881                     return days;
32882                 })(),
32883                 fields : [ 'value' ]
32884             }),
32885             listeners : {
32886                 select : function (_self, record, index)
32887                 {
32888                     _this.setValue(_this.getValue());
32889                 }
32890             }
32891         });
32892
32893         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
32894         
32895         this.monthField = new Roo.bootstrap.MonthField({
32896             after : '<i class=\"fa fa-calendar\"></i>',
32897             allowBlank : this.monthAllowBlank,
32898             placeholder : this.monthPlaceholder,
32899             readOnly : true,
32900             listeners : {
32901                 render : function (_self)
32902                 {
32903                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
32904                         e.preventDefault();
32905                         _self.focus();
32906                     });
32907                 },
32908                 select : function (_self, oldvalue, newvalue)
32909                 {
32910                     _this.setValue(_this.getValue());
32911                 }
32912             }
32913         });
32914         
32915         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
32916         
32917         this.yearField = new Roo.bootstrap.ComboBox({
32918             allowBlank : this.yearAllowBlank,
32919             alwaysQuery : true,
32920             displayField : 'value',
32921             editable : false,
32922             fieldLabel : '',
32923             forceSelection : true,
32924             mode : 'local',
32925             placeholder : this.yearPlaceholder,
32926             selectOnFocus : true,
32927             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32928             triggerAction : 'all',
32929             typeAhead : true,
32930             valueField : 'value',
32931             store : new Roo.data.SimpleStore({
32932                 data : (function() {
32933                     var years = [];
32934                     _this.fireEvent('years', _this, years);
32935                     return years;
32936                 })(),
32937                 fields : [ 'value' ]
32938             }),
32939             listeners : {
32940                 select : function (_self, record, index)
32941                 {
32942                     _this.setValue(_this.getValue());
32943                 }
32944             }
32945         });
32946
32947         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
32948     },
32949     
32950     setValue : function(v, format)
32951     {
32952         this.inputEl.dom.value = v;
32953         
32954         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
32955         
32956         var d = Date.parseDate(v, f);
32957         
32958         if(!d){
32959             this.validate();
32960             return;
32961         }
32962         
32963         this.setDay(d.format(this.dayFormat));
32964         this.setMonth(d.format(this.monthFormat));
32965         this.setYear(d.format(this.yearFormat));
32966         
32967         this.validate();
32968         
32969         return;
32970     },
32971     
32972     setDay : function(v)
32973     {
32974         this.dayField.setValue(v);
32975         this.inputEl.dom.value = this.getValue();
32976         this.validate();
32977         return;
32978     },
32979     
32980     setMonth : function(v)
32981     {
32982         this.monthField.setValue(v, true);
32983         this.inputEl.dom.value = this.getValue();
32984         this.validate();
32985         return;
32986     },
32987     
32988     setYear : function(v)
32989     {
32990         this.yearField.setValue(v);
32991         this.inputEl.dom.value = this.getValue();
32992         this.validate();
32993         return;
32994     },
32995     
32996     getDay : function()
32997     {
32998         return this.dayField.getValue();
32999     },
33000     
33001     getMonth : function()
33002     {
33003         return this.monthField.getValue();
33004     },
33005     
33006     getYear : function()
33007     {
33008         return this.yearField.getValue();
33009     },
33010     
33011     getValue : function()
33012     {
33013         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33014         
33015         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33016         
33017         return date;
33018     },
33019     
33020     reset : function()
33021     {
33022         this.setDay('');
33023         this.setMonth('');
33024         this.setYear('');
33025         this.inputEl.dom.value = '';
33026         this.validate();
33027         return;
33028     },
33029     
33030     validate : function()
33031     {
33032         var d = this.dayField.validate();
33033         var m = this.monthField.validate();
33034         var y = this.yearField.validate();
33035         
33036         var valid = true;
33037         
33038         if(
33039                 (!this.dayAllowBlank && !d) ||
33040                 (!this.monthAllowBlank && !m) ||
33041                 (!this.yearAllowBlank && !y)
33042         ){
33043             valid = false;
33044         }
33045         
33046         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33047             return valid;
33048         }
33049         
33050         if(valid){
33051             this.markValid();
33052             return valid;
33053         }
33054         
33055         this.markInvalid();
33056         
33057         return valid;
33058     },
33059     
33060     markValid : function()
33061     {
33062         
33063         var label = this.el.select('label', true).first();
33064         var icon = this.el.select('i.fa-star', true).first();
33065
33066         if(label && icon){
33067             icon.remove();
33068         }
33069         
33070         this.fireEvent('valid', this);
33071     },
33072     
33073      /**
33074      * Mark this field as invalid
33075      * @param {String} msg The validation message
33076      */
33077     markInvalid : function(msg)
33078     {
33079         
33080         var label = this.el.select('label', true).first();
33081         var icon = this.el.select('i.fa-star', true).first();
33082
33083         if(label && !icon){
33084             this.el.select('.roo-date-split-field-label', true).createChild({
33085                 tag : 'i',
33086                 cls : 'text-danger fa fa-lg fa-star',
33087                 tooltip : 'This field is required',
33088                 style : 'margin-right:5px;'
33089             }, label, true);
33090         }
33091         
33092         this.fireEvent('invalid', this, msg);
33093     },
33094     
33095     clearInvalid : function()
33096     {
33097         var label = this.el.select('label', true).first();
33098         var icon = this.el.select('i.fa-star', true).first();
33099
33100         if(label && icon){
33101             icon.remove();
33102         }
33103         
33104         this.fireEvent('valid', this);
33105     },
33106     
33107     getName: function()
33108     {
33109         return this.name;
33110     }
33111     
33112 });
33113
33114  /**
33115  *
33116  * This is based on 
33117  * http://masonry.desandro.com
33118  *
33119  * The idea is to render all the bricks based on vertical width...
33120  *
33121  * The original code extends 'outlayer' - we might need to use that....
33122  * 
33123  */
33124
33125
33126 /**
33127  * @class Roo.bootstrap.LayoutMasonry
33128  * @extends Roo.bootstrap.Component
33129  * Bootstrap Layout Masonry class
33130  * 
33131  * @constructor
33132  * Create a new Element
33133  * @param {Object} config The config object
33134  */
33135
33136 Roo.bootstrap.LayoutMasonry = function(config){
33137     
33138     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33139     
33140     this.bricks = [];
33141     
33142     Roo.bootstrap.LayoutMasonry.register(this);
33143     
33144     this.addEvents({
33145         // raw events
33146         /**
33147          * @event layout
33148          * Fire after layout the items
33149          * @param {Roo.bootstrap.LayoutMasonry} this
33150          * @param {Roo.EventObject} e
33151          */
33152         "layout" : true
33153     });
33154     
33155 };
33156
33157 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33158     
33159     /**
33160      * @cfg {Boolean} isLayoutInstant = no animation?
33161      */   
33162     isLayoutInstant : false, // needed?
33163    
33164     /**
33165      * @cfg {Number} boxWidth  width of the columns
33166      */   
33167     boxWidth : 450,
33168     
33169       /**
33170      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33171      */   
33172     boxHeight : 0,
33173     
33174     /**
33175      * @cfg {Number} padWidth padding below box..
33176      */   
33177     padWidth : 10, 
33178     
33179     /**
33180      * @cfg {Number} gutter gutter width..
33181      */   
33182     gutter : 10,
33183     
33184      /**
33185      * @cfg {Number} maxCols maximum number of columns
33186      */   
33187     
33188     maxCols: 0,
33189     
33190     /**
33191      * @cfg {Boolean} isAutoInitial defalut true
33192      */   
33193     isAutoInitial : true, 
33194     
33195     containerWidth: 0,
33196     
33197     /**
33198      * @cfg {Boolean} isHorizontal defalut false
33199      */   
33200     isHorizontal : false, 
33201
33202     currentSize : null,
33203     
33204     tag: 'div',
33205     
33206     cls: '',
33207     
33208     bricks: null, //CompositeElement
33209     
33210     cols : 1,
33211     
33212     _isLayoutInited : false,
33213     
33214 //    isAlternative : false, // only use for vertical layout...
33215     
33216     /**
33217      * @cfg {Number} alternativePadWidth padding below box..
33218      */   
33219     alternativePadWidth : 50,
33220     
33221     selectedBrick : [],
33222     
33223     getAutoCreate : function(){
33224         
33225         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33226         
33227         var cfg = {
33228             tag: this.tag,
33229             cls: 'blog-masonary-wrapper ' + this.cls,
33230             cn : {
33231                 cls : 'mas-boxes masonary'
33232             }
33233         };
33234         
33235         return cfg;
33236     },
33237     
33238     getChildContainer: function( )
33239     {
33240         if (this.boxesEl) {
33241             return this.boxesEl;
33242         }
33243         
33244         this.boxesEl = this.el.select('.mas-boxes').first();
33245         
33246         return this.boxesEl;
33247     },
33248     
33249     
33250     initEvents : function()
33251     {
33252         var _this = this;
33253         
33254         if(this.isAutoInitial){
33255             Roo.log('hook children rendered');
33256             this.on('childrenrendered', function() {
33257                 Roo.log('children rendered');
33258                 _this.initial();
33259             } ,this);
33260         }
33261     },
33262     
33263     initial : function()
33264     {
33265         this.selectedBrick = [];
33266         
33267         this.currentSize = this.el.getBox(true);
33268         
33269         Roo.EventManager.onWindowResize(this.resize, this); 
33270
33271         if(!this.isAutoInitial){
33272             this.layout();
33273             return;
33274         }
33275         
33276         this.layout();
33277         
33278         return;
33279         //this.layout.defer(500,this);
33280         
33281     },
33282     
33283     resize : function()
33284     {
33285         var cs = this.el.getBox(true);
33286         
33287         if (
33288                 this.currentSize.width == cs.width && 
33289                 this.currentSize.x == cs.x && 
33290                 this.currentSize.height == cs.height && 
33291                 this.currentSize.y == cs.y 
33292         ) {
33293             Roo.log("no change in with or X or Y");
33294             return;
33295         }
33296         
33297         this.currentSize = cs;
33298         
33299         this.layout();
33300         
33301     },
33302     
33303     layout : function()
33304     {   
33305         this._resetLayout();
33306         
33307         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33308         
33309         this.layoutItems( isInstant );
33310       
33311         this._isLayoutInited = true;
33312         
33313         this.fireEvent('layout', this);
33314         
33315     },
33316     
33317     _resetLayout : function()
33318     {
33319         if(this.isHorizontal){
33320             this.horizontalMeasureColumns();
33321             return;
33322         }
33323         
33324         this.verticalMeasureColumns();
33325         
33326     },
33327     
33328     verticalMeasureColumns : function()
33329     {
33330         this.getContainerWidth();
33331         
33332 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33333 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33334 //            return;
33335 //        }
33336         
33337         var boxWidth = this.boxWidth + this.padWidth;
33338         
33339         if(this.containerWidth < this.boxWidth){
33340             boxWidth = this.containerWidth
33341         }
33342         
33343         var containerWidth = this.containerWidth;
33344         
33345         var cols = Math.floor(containerWidth / boxWidth);
33346         
33347         this.cols = Math.max( cols, 1 );
33348         
33349         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33350         
33351         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33352         
33353         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33354         
33355         this.colWidth = boxWidth + avail - this.padWidth;
33356         
33357         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33358         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33359     },
33360     
33361     horizontalMeasureColumns : function()
33362     {
33363         this.getContainerWidth();
33364         
33365         var boxWidth = this.boxWidth;
33366         
33367         if(this.containerWidth < boxWidth){
33368             boxWidth = this.containerWidth;
33369         }
33370         
33371         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33372         
33373         this.el.setHeight(boxWidth);
33374         
33375     },
33376     
33377     getContainerWidth : function()
33378     {
33379         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33380     },
33381     
33382     layoutItems : function( isInstant )
33383     {
33384         Roo.log(this.bricks);
33385         
33386         var items = Roo.apply([], this.bricks);
33387         
33388         if(this.isHorizontal){
33389             this._horizontalLayoutItems( items , isInstant );
33390             return;
33391         }
33392         
33393 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33394 //            this._verticalAlternativeLayoutItems( items , isInstant );
33395 //            return;
33396 //        }
33397         
33398         this._verticalLayoutItems( items , isInstant );
33399         
33400     },
33401     
33402     _verticalLayoutItems : function ( items , isInstant)
33403     {
33404         if ( !items || !items.length ) {
33405             return;
33406         }
33407         
33408         var standard = [
33409             ['xs', 'xs', 'xs', 'tall'],
33410             ['xs', 'xs', 'tall'],
33411             ['xs', 'xs', 'sm'],
33412             ['xs', 'xs', 'xs'],
33413             ['xs', 'tall'],
33414             ['xs', 'sm'],
33415             ['xs', 'xs'],
33416             ['xs'],
33417             
33418             ['sm', 'xs', 'xs'],
33419             ['sm', 'xs'],
33420             ['sm'],
33421             
33422             ['tall', 'xs', 'xs', 'xs'],
33423             ['tall', 'xs', 'xs'],
33424             ['tall', 'xs'],
33425             ['tall']
33426             
33427         ];
33428         
33429         var queue = [];
33430         
33431         var boxes = [];
33432         
33433         var box = [];
33434         
33435         Roo.each(items, function(item, k){
33436             
33437             switch (item.size) {
33438                 // these layouts take up a full box,
33439                 case 'md' :
33440                 case 'md-left' :
33441                 case 'md-right' :
33442                 case 'wide' :
33443                     
33444                     if(box.length){
33445                         boxes.push(box);
33446                         box = [];
33447                     }
33448                     
33449                     boxes.push([item]);
33450                     
33451                     break;
33452                     
33453                 case 'xs' :
33454                 case 'sm' :
33455                 case 'tall' :
33456                     
33457                     box.push(item);
33458                     
33459                     break;
33460                 default :
33461                     break;
33462                     
33463             }
33464             
33465         }, this);
33466         
33467         if(box.length){
33468             boxes.push(box);
33469             box = [];
33470         }
33471         
33472         var filterPattern = function(box, length)
33473         {
33474             if(!box.length){
33475                 return;
33476             }
33477             
33478             var match = false;
33479             
33480             var pattern = box.slice(0, length);
33481             
33482             var format = [];
33483             
33484             Roo.each(pattern, function(i){
33485                 format.push(i.size);
33486             }, this);
33487             
33488             Roo.each(standard, function(s){
33489                 
33490                 if(String(s) != String(format)){
33491                     return;
33492                 }
33493                 
33494                 match = true;
33495                 return false;
33496                 
33497             }, this);
33498             
33499             if(!match && length == 1){
33500                 return;
33501             }
33502             
33503             if(!match){
33504                 filterPattern(box, length - 1);
33505                 return;
33506             }
33507                 
33508             queue.push(pattern);
33509
33510             box = box.slice(length, box.length);
33511
33512             filterPattern(box, 4);
33513
33514             return;
33515             
33516         }
33517         
33518         Roo.each(boxes, function(box, k){
33519             
33520             if(!box.length){
33521                 return;
33522             }
33523             
33524             if(box.length == 1){
33525                 queue.push(box);
33526                 return;
33527             }
33528             
33529             filterPattern(box, 4);
33530             
33531         }, this);
33532         
33533         this._processVerticalLayoutQueue( queue, isInstant );
33534         
33535     },
33536     
33537 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33538 //    {
33539 //        if ( !items || !items.length ) {
33540 //            return;
33541 //        }
33542 //
33543 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33544 //        
33545 //    },
33546     
33547     _horizontalLayoutItems : function ( items , isInstant)
33548     {
33549         if ( !items || !items.length || items.length < 3) {
33550             return;
33551         }
33552         
33553         items.reverse();
33554         
33555         var eItems = items.slice(0, 3);
33556         
33557         items = items.slice(3, items.length);
33558         
33559         var standard = [
33560             ['xs', 'xs', 'xs', 'wide'],
33561             ['xs', 'xs', 'wide'],
33562             ['xs', 'xs', 'sm'],
33563             ['xs', 'xs', 'xs'],
33564             ['xs', 'wide'],
33565             ['xs', 'sm'],
33566             ['xs', 'xs'],
33567             ['xs'],
33568             
33569             ['sm', 'xs', 'xs'],
33570             ['sm', 'xs'],
33571             ['sm'],
33572             
33573             ['wide', 'xs', 'xs', 'xs'],
33574             ['wide', 'xs', 'xs'],
33575             ['wide', 'xs'],
33576             ['wide'],
33577             
33578             ['wide-thin']
33579         ];
33580         
33581         var queue = [];
33582         
33583         var boxes = [];
33584         
33585         var box = [];
33586         
33587         Roo.each(items, function(item, k){
33588             
33589             switch (item.size) {
33590                 case 'md' :
33591                 case 'md-left' :
33592                 case 'md-right' :
33593                 case 'tall' :
33594                     
33595                     if(box.length){
33596                         boxes.push(box);
33597                         box = [];
33598                     }
33599                     
33600                     boxes.push([item]);
33601                     
33602                     break;
33603                     
33604                 case 'xs' :
33605                 case 'sm' :
33606                 case 'wide' :
33607                 case 'wide-thin' :
33608                     
33609                     box.push(item);
33610                     
33611                     break;
33612                 default :
33613                     break;
33614                     
33615             }
33616             
33617         }, this);
33618         
33619         if(box.length){
33620             boxes.push(box);
33621             box = [];
33622         }
33623         
33624         var filterPattern = function(box, length)
33625         {
33626             if(!box.length){
33627                 return;
33628             }
33629             
33630             var match = false;
33631             
33632             var pattern = box.slice(0, length);
33633             
33634             var format = [];
33635             
33636             Roo.each(pattern, function(i){
33637                 format.push(i.size);
33638             }, this);
33639             
33640             Roo.each(standard, function(s){
33641                 
33642                 if(String(s) != String(format)){
33643                     return;
33644                 }
33645                 
33646                 match = true;
33647                 return false;
33648                 
33649             }, this);
33650             
33651             if(!match && length == 1){
33652                 return;
33653             }
33654             
33655             if(!match){
33656                 filterPattern(box, length - 1);
33657                 return;
33658             }
33659                 
33660             queue.push(pattern);
33661
33662             box = box.slice(length, box.length);
33663
33664             filterPattern(box, 4);
33665
33666             return;
33667             
33668         }
33669         
33670         Roo.each(boxes, function(box, k){
33671             
33672             if(!box.length){
33673                 return;
33674             }
33675             
33676             if(box.length == 1){
33677                 queue.push(box);
33678                 return;
33679             }
33680             
33681             filterPattern(box, 4);
33682             
33683         }, this);
33684         
33685         
33686         var prune = [];
33687         
33688         var pos = this.el.getBox(true);
33689         
33690         var minX = pos.x;
33691         
33692         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33693         
33694         var hit_end = false;
33695         
33696         Roo.each(queue, function(box){
33697             
33698             if(hit_end){
33699                 
33700                 Roo.each(box, function(b){
33701                 
33702                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33703                     b.el.hide();
33704
33705                 }, this);
33706
33707                 return;
33708             }
33709             
33710             var mx = 0;
33711             
33712             Roo.each(box, function(b){
33713                 
33714                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33715                 b.el.show();
33716
33717                 mx = Math.max(mx, b.x);
33718                 
33719             }, this);
33720             
33721             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33722             
33723             if(maxX < minX){
33724                 
33725                 Roo.each(box, function(b){
33726                 
33727                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33728                     b.el.hide();
33729                     
33730                 }, this);
33731                 
33732                 hit_end = true;
33733                 
33734                 return;
33735             }
33736             
33737             prune.push(box);
33738             
33739         }, this);
33740         
33741         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33742     },
33743     
33744     /** Sets position of item in DOM
33745     * @param {Element} item
33746     * @param {Number} x - horizontal position
33747     * @param {Number} y - vertical position
33748     * @param {Boolean} isInstant - disables transitions
33749     */
33750     _processVerticalLayoutQueue : function( queue, isInstant )
33751     {
33752         var pos = this.el.getBox(true);
33753         var x = pos.x;
33754         var y = pos.y;
33755         var maxY = [];
33756         
33757         for (var i = 0; i < this.cols; i++){
33758             maxY[i] = pos.y;
33759         }
33760         
33761         Roo.each(queue, function(box, k){
33762             
33763             var col = k % this.cols;
33764             
33765             Roo.each(box, function(b,kk){
33766                 
33767                 b.el.position('absolute');
33768                 
33769                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33770                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33771                 
33772                 if(b.size == 'md-left' || b.size == 'md-right'){
33773                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33774                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33775                 }
33776                 
33777                 b.el.setWidth(width);
33778                 b.el.setHeight(height);
33779                 // iframe?
33780                 b.el.select('iframe',true).setSize(width,height);
33781                 
33782             }, this);
33783             
33784             for (var i = 0; i < this.cols; i++){
33785                 
33786                 if(maxY[i] < maxY[col]){
33787                     col = i;
33788                     continue;
33789                 }
33790                 
33791                 col = Math.min(col, i);
33792                 
33793             }
33794             
33795             x = pos.x + col * (this.colWidth + this.padWidth);
33796             
33797             y = maxY[col];
33798             
33799             var positions = [];
33800             
33801             switch (box.length){
33802                 case 1 :
33803                     positions = this.getVerticalOneBoxColPositions(x, y, box);
33804                     break;
33805                 case 2 :
33806                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
33807                     break;
33808                 case 3 :
33809                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
33810                     break;
33811                 case 4 :
33812                     positions = this.getVerticalFourBoxColPositions(x, y, box);
33813                     break;
33814                 default :
33815                     break;
33816             }
33817             
33818             Roo.each(box, function(b,kk){
33819                 
33820                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33821                 
33822                 var sz = b.el.getSize();
33823                 
33824                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
33825                 
33826             }, this);
33827             
33828         }, this);
33829         
33830         var mY = 0;
33831         
33832         for (var i = 0; i < this.cols; i++){
33833             mY = Math.max(mY, maxY[i]);
33834         }
33835         
33836         this.el.setHeight(mY - pos.y);
33837         
33838     },
33839     
33840 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
33841 //    {
33842 //        var pos = this.el.getBox(true);
33843 //        var x = pos.x;
33844 //        var y = pos.y;
33845 //        var maxX = pos.right;
33846 //        
33847 //        var maxHeight = 0;
33848 //        
33849 //        Roo.each(items, function(item, k){
33850 //            
33851 //            var c = k % 2;
33852 //            
33853 //            item.el.position('absolute');
33854 //                
33855 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
33856 //
33857 //            item.el.setWidth(width);
33858 //
33859 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
33860 //
33861 //            item.el.setHeight(height);
33862 //            
33863 //            if(c == 0){
33864 //                item.el.setXY([x, y], isInstant ? false : true);
33865 //            } else {
33866 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
33867 //            }
33868 //            
33869 //            y = y + height + this.alternativePadWidth;
33870 //            
33871 //            maxHeight = maxHeight + height + this.alternativePadWidth;
33872 //            
33873 //        }, this);
33874 //        
33875 //        this.el.setHeight(maxHeight);
33876 //        
33877 //    },
33878     
33879     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
33880     {
33881         var pos = this.el.getBox(true);
33882         
33883         var minX = pos.x;
33884         var minY = pos.y;
33885         
33886         var maxX = pos.right;
33887         
33888         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
33889         
33890         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33891         
33892         Roo.each(queue, function(box, k){
33893             
33894             Roo.each(box, function(b, kk){
33895                 
33896                 b.el.position('absolute');
33897                 
33898                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33899                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33900                 
33901                 if(b.size == 'md-left' || b.size == 'md-right'){
33902                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33903                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33904                 }
33905                 
33906                 b.el.setWidth(width);
33907                 b.el.setHeight(height);
33908                 
33909             }, this);
33910             
33911             if(!box.length){
33912                 return;
33913             }
33914             
33915             var positions = [];
33916             
33917             switch (box.length){
33918                 case 1 :
33919                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
33920                     break;
33921                 case 2 :
33922                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
33923                     break;
33924                 case 3 :
33925                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
33926                     break;
33927                 case 4 :
33928                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
33929                     break;
33930                 default :
33931                     break;
33932             }
33933             
33934             Roo.each(box, function(b,kk){
33935                 
33936                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33937                 
33938                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
33939                 
33940             }, this);
33941             
33942         }, this);
33943         
33944     },
33945     
33946     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
33947     {
33948         Roo.each(eItems, function(b,k){
33949             
33950             b.size = (k == 0) ? 'sm' : 'xs';
33951             b.x = (k == 0) ? 2 : 1;
33952             b.y = (k == 0) ? 2 : 1;
33953             
33954             b.el.position('absolute');
33955             
33956             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33957                 
33958             b.el.setWidth(width);
33959             
33960             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33961             
33962             b.el.setHeight(height);
33963             
33964         }, this);
33965
33966         var positions = [];
33967         
33968         positions.push({
33969             x : maxX - this.unitWidth * 2 - this.gutter,
33970             y : minY
33971         });
33972         
33973         positions.push({
33974             x : maxX - this.unitWidth,
33975             y : minY + (this.unitWidth + this.gutter) * 2
33976         });
33977         
33978         positions.push({
33979             x : maxX - this.unitWidth * 3 - this.gutter * 2,
33980             y : minY
33981         });
33982         
33983         Roo.each(eItems, function(b,k){
33984             
33985             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
33986
33987         }, this);
33988         
33989     },
33990     
33991     getVerticalOneBoxColPositions : function(x, y, box)
33992     {
33993         var pos = [];
33994         
33995         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
33996         
33997         if(box[0].size == 'md-left'){
33998             rand = 0;
33999         }
34000         
34001         if(box[0].size == 'md-right'){
34002             rand = 1;
34003         }
34004         
34005         pos.push({
34006             x : x + (this.unitWidth + this.gutter) * rand,
34007             y : y
34008         });
34009         
34010         return pos;
34011     },
34012     
34013     getVerticalTwoBoxColPositions : function(x, y, box)
34014     {
34015         var pos = [];
34016         
34017         if(box[0].size == 'xs'){
34018             
34019             pos.push({
34020                 x : x,
34021                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34022             });
34023
34024             pos.push({
34025                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34026                 y : y
34027             });
34028             
34029             return pos;
34030             
34031         }
34032         
34033         pos.push({
34034             x : x,
34035             y : y
34036         });
34037
34038         pos.push({
34039             x : x + (this.unitWidth + this.gutter) * 2,
34040             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34041         });
34042         
34043         return pos;
34044         
34045     },
34046     
34047     getVerticalThreeBoxColPositions : function(x, y, box)
34048     {
34049         var pos = [];
34050         
34051         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34052             
34053             pos.push({
34054                 x : x,
34055                 y : y
34056             });
34057
34058             pos.push({
34059                 x : x + (this.unitWidth + this.gutter) * 1,
34060                 y : y
34061             });
34062             
34063             pos.push({
34064                 x : x + (this.unitWidth + this.gutter) * 2,
34065                 y : y
34066             });
34067             
34068             return pos;
34069             
34070         }
34071         
34072         if(box[0].size == 'xs' && box[1].size == 'xs'){
34073             
34074             pos.push({
34075                 x : x,
34076                 y : y
34077             });
34078
34079             pos.push({
34080                 x : x,
34081                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34082             });
34083             
34084             pos.push({
34085                 x : x + (this.unitWidth + this.gutter) * 1,
34086                 y : y
34087             });
34088             
34089             return pos;
34090             
34091         }
34092         
34093         pos.push({
34094             x : x,
34095             y : y
34096         });
34097
34098         pos.push({
34099             x : x + (this.unitWidth + this.gutter) * 2,
34100             y : y
34101         });
34102
34103         pos.push({
34104             x : x + (this.unitWidth + this.gutter) * 2,
34105             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34106         });
34107             
34108         return pos;
34109         
34110     },
34111     
34112     getVerticalFourBoxColPositions : function(x, y, box)
34113     {
34114         var pos = [];
34115         
34116         if(box[0].size == 'xs'){
34117             
34118             pos.push({
34119                 x : x,
34120                 y : y
34121             });
34122
34123             pos.push({
34124                 x : x,
34125                 y : y + (this.unitHeight + this.gutter) * 1
34126             });
34127             
34128             pos.push({
34129                 x : x,
34130                 y : y + (this.unitHeight + this.gutter) * 2
34131             });
34132             
34133             pos.push({
34134                 x : x + (this.unitWidth + this.gutter) * 1,
34135                 y : y
34136             });
34137             
34138             return pos;
34139             
34140         }
34141         
34142         pos.push({
34143             x : x,
34144             y : y
34145         });
34146
34147         pos.push({
34148             x : x + (this.unitWidth + this.gutter) * 2,
34149             y : y
34150         });
34151
34152         pos.push({
34153             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34154             y : y + (this.unitHeight + this.gutter) * 1
34155         });
34156
34157         pos.push({
34158             x : x + (this.unitWidth + this.gutter) * 2,
34159             y : y + (this.unitWidth + this.gutter) * 2
34160         });
34161
34162         return pos;
34163         
34164     },
34165     
34166     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34167     {
34168         var pos = [];
34169         
34170         if(box[0].size == 'md-left'){
34171             pos.push({
34172                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34173                 y : minY
34174             });
34175             
34176             return pos;
34177         }
34178         
34179         if(box[0].size == 'md-right'){
34180             pos.push({
34181                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34182                 y : minY + (this.unitWidth + this.gutter) * 1
34183             });
34184             
34185             return pos;
34186         }
34187         
34188         var rand = Math.floor(Math.random() * (4 - box[0].y));
34189         
34190         pos.push({
34191             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34192             y : minY + (this.unitWidth + this.gutter) * rand
34193         });
34194         
34195         return pos;
34196         
34197     },
34198     
34199     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34200     {
34201         var pos = [];
34202         
34203         if(box[0].size == 'xs'){
34204             
34205             pos.push({
34206                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34207                 y : minY
34208             });
34209
34210             pos.push({
34211                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34212                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34213             });
34214             
34215             return pos;
34216             
34217         }
34218         
34219         pos.push({
34220             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34221             y : minY
34222         });
34223
34224         pos.push({
34225             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34226             y : minY + (this.unitWidth + this.gutter) * 2
34227         });
34228         
34229         return pos;
34230         
34231     },
34232     
34233     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34234     {
34235         var pos = [];
34236         
34237         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34238             
34239             pos.push({
34240                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34241                 y : minY
34242             });
34243
34244             pos.push({
34245                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34246                 y : minY + (this.unitWidth + this.gutter) * 1
34247             });
34248             
34249             pos.push({
34250                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34251                 y : minY + (this.unitWidth + this.gutter) * 2
34252             });
34253             
34254             return pos;
34255             
34256         }
34257         
34258         if(box[0].size == 'xs' && box[1].size == 'xs'){
34259             
34260             pos.push({
34261                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34262                 y : minY
34263             });
34264
34265             pos.push({
34266                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34267                 y : minY
34268             });
34269             
34270             pos.push({
34271                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34272                 y : minY + (this.unitWidth + this.gutter) * 1
34273             });
34274             
34275             return pos;
34276             
34277         }
34278         
34279         pos.push({
34280             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34281             y : minY
34282         });
34283
34284         pos.push({
34285             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34286             y : minY + (this.unitWidth + this.gutter) * 2
34287         });
34288
34289         pos.push({
34290             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34291             y : minY + (this.unitWidth + this.gutter) * 2
34292         });
34293             
34294         return pos;
34295         
34296     },
34297     
34298     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34299     {
34300         var pos = [];
34301         
34302         if(box[0].size == 'xs'){
34303             
34304             pos.push({
34305                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34306                 y : minY
34307             });
34308
34309             pos.push({
34310                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34311                 y : minY
34312             });
34313             
34314             pos.push({
34315                 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),
34316                 y : minY
34317             });
34318             
34319             pos.push({
34320                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34321                 y : minY + (this.unitWidth + this.gutter) * 1
34322             });
34323             
34324             return pos;
34325             
34326         }
34327         
34328         pos.push({
34329             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34330             y : minY
34331         });
34332         
34333         pos.push({
34334             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34335             y : minY + (this.unitWidth + this.gutter) * 2
34336         });
34337         
34338         pos.push({
34339             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34340             y : minY + (this.unitWidth + this.gutter) * 2
34341         });
34342         
34343         pos.push({
34344             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),
34345             y : minY + (this.unitWidth + this.gutter) * 2
34346         });
34347
34348         return pos;
34349         
34350     },
34351     
34352     /**
34353     * remove a Masonry Brick
34354     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34355     */
34356     removeBrick : function(brick_id)
34357     {
34358         if (!brick_id) {
34359             return;
34360         }
34361         
34362         for (var i = 0; i<this.bricks.length; i++) {
34363             if (this.bricks[i].id == brick_id) {
34364                 this.bricks.splice(i,1);
34365                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34366                 this.initial();
34367             }
34368         }
34369     },
34370     
34371     /**
34372     * adds a Masonry Brick
34373     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34374     */
34375     addBrick : function(cfg)
34376     {
34377         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34378         //this.register(cn);
34379         cn.parentId = this.id;
34380         cn.render(this.el);
34381         return cn;
34382     },
34383     
34384     /**
34385     * register a Masonry Brick
34386     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34387     */
34388     
34389     register : function(brick)
34390     {
34391         this.bricks.push(brick);
34392         brick.masonryId = this.id;
34393     },
34394     
34395     /**
34396     * clear all the Masonry Brick
34397     */
34398     clearAll : function()
34399     {
34400         this.bricks = [];
34401         //this.getChildContainer().dom.innerHTML = "";
34402         this.el.dom.innerHTML = '';
34403     },
34404     
34405     getSelected : function()
34406     {
34407         if (!this.selectedBrick) {
34408             return false;
34409         }
34410         
34411         return this.selectedBrick;
34412     }
34413 });
34414
34415 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34416     
34417     groups: {},
34418      /**
34419     * register a Masonry Layout
34420     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34421     */
34422     
34423     register : function(layout)
34424     {
34425         this.groups[layout.id] = layout;
34426     },
34427     /**
34428     * fetch a  Masonry Layout based on the masonry layout ID
34429     * @param {string} the masonry layout to add
34430     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34431     */
34432     
34433     get: function(layout_id) {
34434         if (typeof(this.groups[layout_id]) == 'undefined') {
34435             return false;
34436         }
34437         return this.groups[layout_id] ;
34438     }
34439     
34440     
34441     
34442 });
34443
34444  
34445
34446  /**
34447  *
34448  * This is based on 
34449  * http://masonry.desandro.com
34450  *
34451  * The idea is to render all the bricks based on vertical width...
34452  *
34453  * The original code extends 'outlayer' - we might need to use that....
34454  * 
34455  */
34456
34457
34458 /**
34459  * @class Roo.bootstrap.LayoutMasonryAuto
34460  * @extends Roo.bootstrap.Component
34461  * Bootstrap Layout Masonry class
34462  * 
34463  * @constructor
34464  * Create a new Element
34465  * @param {Object} config The config object
34466  */
34467
34468 Roo.bootstrap.LayoutMasonryAuto = function(config){
34469     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34470 };
34471
34472 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34473     
34474       /**
34475      * @cfg {Boolean} isFitWidth  - resize the width..
34476      */   
34477     isFitWidth : false,  // options..
34478     /**
34479      * @cfg {Boolean} isOriginLeft = left align?
34480      */   
34481     isOriginLeft : true,
34482     /**
34483      * @cfg {Boolean} isOriginTop = top align?
34484      */   
34485     isOriginTop : false,
34486     /**
34487      * @cfg {Boolean} isLayoutInstant = no animation?
34488      */   
34489     isLayoutInstant : false, // needed?
34490     /**
34491      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34492      */   
34493     isResizingContainer : true,
34494     /**
34495      * @cfg {Number} columnWidth  width of the columns 
34496      */   
34497     
34498     columnWidth : 0,
34499     
34500     /**
34501      * @cfg {Number} maxCols maximum number of columns
34502      */   
34503     
34504     maxCols: 0,
34505     /**
34506      * @cfg {Number} padHeight padding below box..
34507      */   
34508     
34509     padHeight : 10, 
34510     
34511     /**
34512      * @cfg {Boolean} isAutoInitial defalut true
34513      */   
34514     
34515     isAutoInitial : true, 
34516     
34517     // private?
34518     gutter : 0,
34519     
34520     containerWidth: 0,
34521     initialColumnWidth : 0,
34522     currentSize : null,
34523     
34524     colYs : null, // array.
34525     maxY : 0,
34526     padWidth: 10,
34527     
34528     
34529     tag: 'div',
34530     cls: '',
34531     bricks: null, //CompositeElement
34532     cols : 0, // array?
34533     // element : null, // wrapped now this.el
34534     _isLayoutInited : null, 
34535     
34536     
34537     getAutoCreate : function(){
34538         
34539         var cfg = {
34540             tag: this.tag,
34541             cls: 'blog-masonary-wrapper ' + this.cls,
34542             cn : {
34543                 cls : 'mas-boxes masonary'
34544             }
34545         };
34546         
34547         return cfg;
34548     },
34549     
34550     getChildContainer: function( )
34551     {
34552         if (this.boxesEl) {
34553             return this.boxesEl;
34554         }
34555         
34556         this.boxesEl = this.el.select('.mas-boxes').first();
34557         
34558         return this.boxesEl;
34559     },
34560     
34561     
34562     initEvents : function()
34563     {
34564         var _this = this;
34565         
34566         if(this.isAutoInitial){
34567             Roo.log('hook children rendered');
34568             this.on('childrenrendered', function() {
34569                 Roo.log('children rendered');
34570                 _this.initial();
34571             } ,this);
34572         }
34573         
34574     },
34575     
34576     initial : function()
34577     {
34578         this.reloadItems();
34579
34580         this.currentSize = this.el.getBox(true);
34581
34582         /// was window resize... - let's see if this works..
34583         Roo.EventManager.onWindowResize(this.resize, this); 
34584
34585         if(!this.isAutoInitial){
34586             this.layout();
34587             return;
34588         }
34589         
34590         this.layout.defer(500,this);
34591     },
34592     
34593     reloadItems: function()
34594     {
34595         this.bricks = this.el.select('.masonry-brick', true);
34596         
34597         this.bricks.each(function(b) {
34598             //Roo.log(b.getSize());
34599             if (!b.attr('originalwidth')) {
34600                 b.attr('originalwidth',  b.getSize().width);
34601             }
34602             
34603         });
34604         
34605         Roo.log(this.bricks.elements.length);
34606     },
34607     
34608     resize : function()
34609     {
34610         Roo.log('resize');
34611         var cs = this.el.getBox(true);
34612         
34613         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34614             Roo.log("no change in with or X");
34615             return;
34616         }
34617         this.currentSize = cs;
34618         this.layout();
34619     },
34620     
34621     layout : function()
34622     {
34623          Roo.log('layout');
34624         this._resetLayout();
34625         //this._manageStamps();
34626       
34627         // don't animate first layout
34628         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34629         this.layoutItems( isInstant );
34630       
34631         // flag for initalized
34632         this._isLayoutInited = true;
34633     },
34634     
34635     layoutItems : function( isInstant )
34636     {
34637         //var items = this._getItemsForLayout( this.items );
34638         // original code supports filtering layout items.. we just ignore it..
34639         
34640         this._layoutItems( this.bricks , isInstant );
34641       
34642         this._postLayout();
34643     },
34644     _layoutItems : function ( items , isInstant)
34645     {
34646        //this.fireEvent( 'layout', this, items );
34647     
34648
34649         if ( !items || !items.elements.length ) {
34650           // no items, emit event with empty array
34651             return;
34652         }
34653
34654         var queue = [];
34655         items.each(function(item) {
34656             Roo.log("layout item");
34657             Roo.log(item);
34658             // get x/y object from method
34659             var position = this._getItemLayoutPosition( item );
34660             // enqueue
34661             position.item = item;
34662             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34663             queue.push( position );
34664         }, this);
34665       
34666         this._processLayoutQueue( queue );
34667     },
34668     /** Sets position of item in DOM
34669     * @param {Element} item
34670     * @param {Number} x - horizontal position
34671     * @param {Number} y - vertical position
34672     * @param {Boolean} isInstant - disables transitions
34673     */
34674     _processLayoutQueue : function( queue )
34675     {
34676         for ( var i=0, len = queue.length; i < len; i++ ) {
34677             var obj = queue[i];
34678             obj.item.position('absolute');
34679             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34680         }
34681     },
34682       
34683     
34684     /**
34685     * Any logic you want to do after each layout,
34686     * i.e. size the container
34687     */
34688     _postLayout : function()
34689     {
34690         this.resizeContainer();
34691     },
34692     
34693     resizeContainer : function()
34694     {
34695         if ( !this.isResizingContainer ) {
34696             return;
34697         }
34698         var size = this._getContainerSize();
34699         if ( size ) {
34700             this.el.setSize(size.width,size.height);
34701             this.boxesEl.setSize(size.width,size.height);
34702         }
34703     },
34704     
34705     
34706     
34707     _resetLayout : function()
34708     {
34709         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34710         this.colWidth = this.el.getWidth();
34711         //this.gutter = this.el.getWidth(); 
34712         
34713         this.measureColumns();
34714
34715         // reset column Y
34716         var i = this.cols;
34717         this.colYs = [];
34718         while (i--) {
34719             this.colYs.push( 0 );
34720         }
34721     
34722         this.maxY = 0;
34723     },
34724
34725     measureColumns : function()
34726     {
34727         this.getContainerWidth();
34728       // if columnWidth is 0, default to outerWidth of first item
34729         if ( !this.columnWidth ) {
34730             var firstItem = this.bricks.first();
34731             Roo.log(firstItem);
34732             this.columnWidth  = this.containerWidth;
34733             if (firstItem && firstItem.attr('originalwidth') ) {
34734                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34735             }
34736             // columnWidth fall back to item of first element
34737             Roo.log("set column width?");
34738                         this.initialColumnWidth = this.columnWidth  ;
34739
34740             // if first elem has no width, default to size of container
34741             
34742         }
34743         
34744         
34745         if (this.initialColumnWidth) {
34746             this.columnWidth = this.initialColumnWidth;
34747         }
34748         
34749         
34750             
34751         // column width is fixed at the top - however if container width get's smaller we should
34752         // reduce it...
34753         
34754         // this bit calcs how man columns..
34755             
34756         var columnWidth = this.columnWidth += this.gutter;
34757       
34758         // calculate columns
34759         var containerWidth = this.containerWidth + this.gutter;
34760         
34761         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34762         // fix rounding errors, typically with gutters
34763         var excess = columnWidth - containerWidth % columnWidth;
34764         
34765         
34766         // if overshoot is less than a pixel, round up, otherwise floor it
34767         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34768         cols = Math[ mathMethod ]( cols );
34769         this.cols = Math.max( cols, 1 );
34770         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34771         
34772          // padding positioning..
34773         var totalColWidth = this.cols * this.columnWidth;
34774         var padavail = this.containerWidth - totalColWidth;
34775         // so for 2 columns - we need 3 'pads'
34776         
34777         var padNeeded = (1+this.cols) * this.padWidth;
34778         
34779         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34780         
34781         this.columnWidth += padExtra
34782         //this.padWidth = Math.floor(padavail /  ( this.cols));
34783         
34784         // adjust colum width so that padding is fixed??
34785         
34786         // we have 3 columns ... total = width * 3
34787         // we have X left over... that should be used by 
34788         
34789         //if (this.expandC) {
34790             
34791         //}
34792         
34793         
34794         
34795     },
34796     
34797     getContainerWidth : function()
34798     {
34799        /* // container is parent if fit width
34800         var container = this.isFitWidth ? this.element.parentNode : this.element;
34801         // check that this.size and size are there
34802         // IE8 triggers resize on body size change, so they might not be
34803         
34804         var size = getSize( container );  //FIXME
34805         this.containerWidth = size && size.innerWidth; //FIXME
34806         */
34807          
34808         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34809         
34810     },
34811     
34812     _getItemLayoutPosition : function( item )  // what is item?
34813     {
34814         // we resize the item to our columnWidth..
34815       
34816         item.setWidth(this.columnWidth);
34817         item.autoBoxAdjust  = false;
34818         
34819         var sz = item.getSize();
34820  
34821         // how many columns does this brick span
34822         var remainder = this.containerWidth % this.columnWidth;
34823         
34824         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
34825         // round if off by 1 pixel, otherwise use ceil
34826         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
34827         colSpan = Math.min( colSpan, this.cols );
34828         
34829         // normally this should be '1' as we dont' currently allow multi width columns..
34830         
34831         var colGroup = this._getColGroup( colSpan );
34832         // get the minimum Y value from the columns
34833         var minimumY = Math.min.apply( Math, colGroup );
34834         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34835         
34836         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
34837          
34838         // position the brick
34839         var position = {
34840             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
34841             y: this.currentSize.y + minimumY + this.padHeight
34842         };
34843         
34844         Roo.log(position);
34845         // apply setHeight to necessary columns
34846         var setHeight = minimumY + sz.height + this.padHeight;
34847         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34848         
34849         var setSpan = this.cols + 1 - colGroup.length;
34850         for ( var i = 0; i < setSpan; i++ ) {
34851           this.colYs[ shortColIndex + i ] = setHeight ;
34852         }
34853       
34854         return position;
34855     },
34856     
34857     /**
34858      * @param {Number} colSpan - number of columns the element spans
34859      * @returns {Array} colGroup
34860      */
34861     _getColGroup : function( colSpan )
34862     {
34863         if ( colSpan < 2 ) {
34864           // if brick spans only one column, use all the column Ys
34865           return this.colYs;
34866         }
34867       
34868         var colGroup = [];
34869         // how many different places could this brick fit horizontally
34870         var groupCount = this.cols + 1 - colSpan;
34871         // for each group potential horizontal position
34872         for ( var i = 0; i < groupCount; i++ ) {
34873           // make an array of colY values for that one group
34874           var groupColYs = this.colYs.slice( i, i + colSpan );
34875           // and get the max value of the array
34876           colGroup[i] = Math.max.apply( Math, groupColYs );
34877         }
34878         return colGroup;
34879     },
34880     /*
34881     _manageStamp : function( stamp )
34882     {
34883         var stampSize =  stamp.getSize();
34884         var offset = stamp.getBox();
34885         // get the columns that this stamp affects
34886         var firstX = this.isOriginLeft ? offset.x : offset.right;
34887         var lastX = firstX + stampSize.width;
34888         var firstCol = Math.floor( firstX / this.columnWidth );
34889         firstCol = Math.max( 0, firstCol );
34890         
34891         var lastCol = Math.floor( lastX / this.columnWidth );
34892         // lastCol should not go over if multiple of columnWidth #425
34893         lastCol -= lastX % this.columnWidth ? 0 : 1;
34894         lastCol = Math.min( this.cols - 1, lastCol );
34895         
34896         // set colYs to bottom of the stamp
34897         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
34898             stampSize.height;
34899             
34900         for ( var i = firstCol; i <= lastCol; i++ ) {
34901           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
34902         }
34903     },
34904     */
34905     
34906     _getContainerSize : function()
34907     {
34908         this.maxY = Math.max.apply( Math, this.colYs );
34909         var size = {
34910             height: this.maxY
34911         };
34912       
34913         if ( this.isFitWidth ) {
34914             size.width = this._getContainerFitWidth();
34915         }
34916       
34917         return size;
34918     },
34919     
34920     _getContainerFitWidth : function()
34921     {
34922         var unusedCols = 0;
34923         // count unused columns
34924         var i = this.cols;
34925         while ( --i ) {
34926           if ( this.colYs[i] !== 0 ) {
34927             break;
34928           }
34929           unusedCols++;
34930         }
34931         // fit container to columns that have been used
34932         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
34933     },
34934     
34935     needsResizeLayout : function()
34936     {
34937         var previousWidth = this.containerWidth;
34938         this.getContainerWidth();
34939         return previousWidth !== this.containerWidth;
34940     }
34941  
34942 });
34943
34944  
34945
34946  /*
34947  * - LGPL
34948  *
34949  * element
34950  * 
34951  */
34952
34953 /**
34954  * @class Roo.bootstrap.MasonryBrick
34955  * @extends Roo.bootstrap.Component
34956  * Bootstrap MasonryBrick class
34957  * 
34958  * @constructor
34959  * Create a new MasonryBrick
34960  * @param {Object} config The config object
34961  */
34962
34963 Roo.bootstrap.MasonryBrick = function(config){
34964     
34965     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
34966     
34967     Roo.bootstrap.MasonryBrick.register(this);
34968     
34969     this.addEvents({
34970         // raw events
34971         /**
34972          * @event click
34973          * When a MasonryBrick is clcik
34974          * @param {Roo.bootstrap.MasonryBrick} this
34975          * @param {Roo.EventObject} e
34976          */
34977         "click" : true
34978     });
34979 };
34980
34981 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
34982     
34983     /**
34984      * @cfg {String} title
34985      */   
34986     title : '',
34987     /**
34988      * @cfg {String} html
34989      */   
34990     html : '',
34991     /**
34992      * @cfg {String} bgimage
34993      */   
34994     bgimage : '',
34995     /**
34996      * @cfg {String} videourl
34997      */   
34998     videourl : '',
34999     /**
35000      * @cfg {String} cls
35001      */   
35002     cls : '',
35003     /**
35004      * @cfg {String} href
35005      */   
35006     href : '',
35007     /**
35008      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35009      */   
35010     size : 'xs',
35011     
35012     /**
35013      * @cfg {String} placetitle (center|bottom)
35014      */   
35015     placetitle : '',
35016     
35017     /**
35018      * @cfg {Boolean} isFitContainer defalut true
35019      */   
35020     isFitContainer : true, 
35021     
35022     /**
35023      * @cfg {Boolean} preventDefault defalut false
35024      */   
35025     preventDefault : false, 
35026     
35027     /**
35028      * @cfg {Boolean} inverse defalut false
35029      */   
35030     maskInverse : false, 
35031     
35032     getAutoCreate : function()
35033     {
35034         if(!this.isFitContainer){
35035             return this.getSplitAutoCreate();
35036         }
35037         
35038         var cls = 'masonry-brick masonry-brick-full';
35039         
35040         if(this.href.length){
35041             cls += ' masonry-brick-link';
35042         }
35043         
35044         if(this.bgimage.length){
35045             cls += ' masonry-brick-image';
35046         }
35047         
35048         if(this.maskInverse){
35049             cls += ' mask-inverse';
35050         }
35051         
35052         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35053             cls += ' enable-mask';
35054         }
35055         
35056         if(this.size){
35057             cls += ' masonry-' + this.size + '-brick';
35058         }
35059         
35060         if(this.placetitle.length){
35061             
35062             switch (this.placetitle) {
35063                 case 'center' :
35064                     cls += ' masonry-center-title';
35065                     break;
35066                 case 'bottom' :
35067                     cls += ' masonry-bottom-title';
35068                     break;
35069                 default:
35070                     break;
35071             }
35072             
35073         } else {
35074             if(!this.html.length && !this.bgimage.length){
35075                 cls += ' masonry-center-title';
35076             }
35077
35078             if(!this.html.length && this.bgimage.length){
35079                 cls += ' masonry-bottom-title';
35080             }
35081         }
35082         
35083         if(this.cls){
35084             cls += ' ' + this.cls;
35085         }
35086         
35087         var cfg = {
35088             tag: (this.href.length) ? 'a' : 'div',
35089             cls: cls,
35090             cn: [
35091                 {
35092                     tag: 'div',
35093                     cls: 'masonry-brick-mask'
35094                 },
35095                 {
35096                     tag: 'div',
35097                     cls: 'masonry-brick-paragraph',
35098                     cn: []
35099                 }
35100             ]
35101         };
35102         
35103         if(this.href.length){
35104             cfg.href = this.href;
35105         }
35106         
35107         var cn = cfg.cn[1].cn;
35108         
35109         if(this.title.length){
35110             cn.push({
35111                 tag: 'h4',
35112                 cls: 'masonry-brick-title',
35113                 html: this.title
35114             });
35115         }
35116         
35117         if(this.html.length){
35118             cn.push({
35119                 tag: 'p',
35120                 cls: 'masonry-brick-text',
35121                 html: this.html
35122             });
35123         }
35124         
35125         if (!this.title.length && !this.html.length) {
35126             cfg.cn[1].cls += ' hide';
35127         }
35128         
35129         if(this.bgimage.length){
35130             cfg.cn.push({
35131                 tag: 'img',
35132                 cls: 'masonry-brick-image-view',
35133                 src: this.bgimage
35134             });
35135         }
35136         
35137         if(this.videourl.length){
35138             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35139             // youtube support only?
35140             cfg.cn.push({
35141                 tag: 'iframe',
35142                 cls: 'masonry-brick-image-view',
35143                 src: vurl,
35144                 frameborder : 0,
35145                 allowfullscreen : true
35146             });
35147         }
35148         
35149         return cfg;
35150         
35151     },
35152     
35153     getSplitAutoCreate : function()
35154     {
35155         var cls = 'masonry-brick masonry-brick-split';
35156         
35157         if(this.href.length){
35158             cls += ' masonry-brick-link';
35159         }
35160         
35161         if(this.bgimage.length){
35162             cls += ' masonry-brick-image';
35163         }
35164         
35165         if(this.size){
35166             cls += ' masonry-' + this.size + '-brick';
35167         }
35168         
35169         switch (this.placetitle) {
35170             case 'center' :
35171                 cls += ' masonry-center-title';
35172                 break;
35173             case 'bottom' :
35174                 cls += ' masonry-bottom-title';
35175                 break;
35176             default:
35177                 if(!this.bgimage.length){
35178                     cls += ' masonry-center-title';
35179                 }
35180
35181                 if(this.bgimage.length){
35182                     cls += ' masonry-bottom-title';
35183                 }
35184                 break;
35185         }
35186         
35187         if(this.cls){
35188             cls += ' ' + this.cls;
35189         }
35190         
35191         var cfg = {
35192             tag: (this.href.length) ? 'a' : 'div',
35193             cls: cls,
35194             cn: [
35195                 {
35196                     tag: 'div',
35197                     cls: 'masonry-brick-split-head',
35198                     cn: [
35199                         {
35200                             tag: 'div',
35201                             cls: 'masonry-brick-paragraph',
35202                             cn: []
35203                         }
35204                     ]
35205                 },
35206                 {
35207                     tag: 'div',
35208                     cls: 'masonry-brick-split-body',
35209                     cn: []
35210                 }
35211             ]
35212         };
35213         
35214         if(this.href.length){
35215             cfg.href = this.href;
35216         }
35217         
35218         if(this.title.length){
35219             cfg.cn[0].cn[0].cn.push({
35220                 tag: 'h4',
35221                 cls: 'masonry-brick-title',
35222                 html: this.title
35223             });
35224         }
35225         
35226         if(this.html.length){
35227             cfg.cn[1].cn.push({
35228                 tag: 'p',
35229                 cls: 'masonry-brick-text',
35230                 html: this.html
35231             });
35232         }
35233
35234         if(this.bgimage.length){
35235             cfg.cn[0].cn.push({
35236                 tag: 'img',
35237                 cls: 'masonry-brick-image-view',
35238                 src: this.bgimage
35239             });
35240         }
35241         
35242         if(this.videourl.length){
35243             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35244             // youtube support only?
35245             cfg.cn[0].cn.cn.push({
35246                 tag: 'iframe',
35247                 cls: 'masonry-brick-image-view',
35248                 src: vurl,
35249                 frameborder : 0,
35250                 allowfullscreen : true
35251             });
35252         }
35253         
35254         return cfg;
35255     },
35256     
35257     initEvents: function() 
35258     {
35259         switch (this.size) {
35260             case 'xs' :
35261                 this.x = 1;
35262                 this.y = 1;
35263                 break;
35264             case 'sm' :
35265                 this.x = 2;
35266                 this.y = 2;
35267                 break;
35268             case 'md' :
35269             case 'md-left' :
35270             case 'md-right' :
35271                 this.x = 3;
35272                 this.y = 3;
35273                 break;
35274             case 'tall' :
35275                 this.x = 2;
35276                 this.y = 3;
35277                 break;
35278             case 'wide' :
35279                 this.x = 3;
35280                 this.y = 2;
35281                 break;
35282             case 'wide-thin' :
35283                 this.x = 3;
35284                 this.y = 1;
35285                 break;
35286                         
35287             default :
35288                 break;
35289         }
35290         
35291         if(Roo.isTouch){
35292             this.el.on('touchstart', this.onTouchStart, this);
35293             this.el.on('touchmove', this.onTouchMove, this);
35294             this.el.on('touchend', this.onTouchEnd, this);
35295             this.el.on('contextmenu', this.onContextMenu, this);
35296         } else {
35297             this.el.on('mouseenter'  ,this.enter, this);
35298             this.el.on('mouseleave', this.leave, this);
35299             this.el.on('click', this.onClick, this);
35300         }
35301         
35302         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35303             this.parent().bricks.push(this);   
35304         }
35305         
35306     },
35307     
35308     onClick: function(e, el)
35309     {
35310         var time = this.endTimer - this.startTimer;
35311         // Roo.log(e.preventDefault());
35312         if(Roo.isTouch){
35313             if(time > 1000){
35314                 e.preventDefault();
35315                 return;
35316             }
35317         }
35318         
35319         if(!this.preventDefault){
35320             return;
35321         }
35322         
35323         e.preventDefault();
35324         
35325         if (this.activeClass != '') {
35326             this.selectBrick();
35327         }
35328         
35329         this.fireEvent('click', this, e);
35330     },
35331     
35332     enter: function(e, el)
35333     {
35334         e.preventDefault();
35335         
35336         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35337             return;
35338         }
35339         
35340         if(this.bgimage.length && this.html.length){
35341             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35342         }
35343     },
35344     
35345     leave: function(e, el)
35346     {
35347         e.preventDefault();
35348         
35349         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35350             return;
35351         }
35352         
35353         if(this.bgimage.length && this.html.length){
35354             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35355         }
35356     },
35357     
35358     onTouchStart: function(e, el)
35359     {
35360 //        e.preventDefault();
35361         
35362         this.touchmoved = false;
35363         
35364         if(!this.isFitContainer){
35365             return;
35366         }
35367         
35368         if(!this.bgimage.length || !this.html.length){
35369             return;
35370         }
35371         
35372         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35373         
35374         this.timer = new Date().getTime();
35375         
35376     },
35377     
35378     onTouchMove: function(e, el)
35379     {
35380         this.touchmoved = true;
35381     },
35382     
35383     onContextMenu : function(e,el)
35384     {
35385         e.preventDefault();
35386         e.stopPropagation();
35387         return false;
35388     },
35389     
35390     onTouchEnd: function(e, el)
35391     {
35392 //        e.preventDefault();
35393         
35394         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35395         
35396             this.leave(e,el);
35397             
35398             return;
35399         }
35400         
35401         if(!this.bgimage.length || !this.html.length){
35402             
35403             if(this.href.length){
35404                 window.location.href = this.href;
35405             }
35406             
35407             return;
35408         }
35409         
35410         if(!this.isFitContainer){
35411             return;
35412         }
35413         
35414         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35415         
35416         window.location.href = this.href;
35417     },
35418     
35419     //selection on single brick only
35420     selectBrick : function() {
35421         
35422         if (!this.parentId) {
35423             return;
35424         }
35425         
35426         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35427         var index = m.selectedBrick.indexOf(this.id);
35428         
35429         if ( index > -1) {
35430             m.selectedBrick.splice(index,1);
35431             this.el.removeClass(this.activeClass);
35432             return;
35433         }
35434         
35435         for(var i = 0; i < m.selectedBrick.length; i++) {
35436             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35437             b.el.removeClass(b.activeClass);
35438         }
35439         
35440         m.selectedBrick = [];
35441         
35442         m.selectedBrick.push(this.id);
35443         this.el.addClass(this.activeClass);
35444         return;
35445     },
35446     
35447     isSelected : function(){
35448         return this.el.hasClass(this.activeClass);
35449         
35450     }
35451 });
35452
35453 Roo.apply(Roo.bootstrap.MasonryBrick, {
35454     
35455     //groups: {},
35456     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35457      /**
35458     * register a Masonry Brick
35459     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35460     */
35461     
35462     register : function(brick)
35463     {
35464         //this.groups[brick.id] = brick;
35465         this.groups.add(brick.id, brick);
35466     },
35467     /**
35468     * fetch a  masonry brick based on the masonry brick ID
35469     * @param {string} the masonry brick to add
35470     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35471     */
35472     
35473     get: function(brick_id) 
35474     {
35475         // if (typeof(this.groups[brick_id]) == 'undefined') {
35476         //     return false;
35477         // }
35478         // return this.groups[brick_id] ;
35479         
35480         if(this.groups.key(brick_id)) {
35481             return this.groups.key(brick_id);
35482         }
35483         
35484         return false;
35485     }
35486     
35487     
35488     
35489 });
35490
35491  /*
35492  * - LGPL
35493  *
35494  * element
35495  * 
35496  */
35497
35498 /**
35499  * @class Roo.bootstrap.Brick
35500  * @extends Roo.bootstrap.Component
35501  * Bootstrap Brick class
35502  * 
35503  * @constructor
35504  * Create a new Brick
35505  * @param {Object} config The config object
35506  */
35507
35508 Roo.bootstrap.Brick = function(config){
35509     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35510     
35511     this.addEvents({
35512         // raw events
35513         /**
35514          * @event click
35515          * When a Brick is click
35516          * @param {Roo.bootstrap.Brick} this
35517          * @param {Roo.EventObject} e
35518          */
35519         "click" : true
35520     });
35521 };
35522
35523 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35524     
35525     /**
35526      * @cfg {String} title
35527      */   
35528     title : '',
35529     /**
35530      * @cfg {String} html
35531      */   
35532     html : '',
35533     /**
35534      * @cfg {String} bgimage
35535      */   
35536     bgimage : '',
35537     /**
35538      * @cfg {String} cls
35539      */   
35540     cls : '',
35541     /**
35542      * @cfg {String} href
35543      */   
35544     href : '',
35545     /**
35546      * @cfg {String} video
35547      */   
35548     video : '',
35549     /**
35550      * @cfg {Boolean} square
35551      */   
35552     square : true,
35553     
35554     getAutoCreate : function()
35555     {
35556         var cls = 'roo-brick';
35557         
35558         if(this.href.length){
35559             cls += ' roo-brick-link';
35560         }
35561         
35562         if(this.bgimage.length){
35563             cls += ' roo-brick-image';
35564         }
35565         
35566         if(!this.html.length && !this.bgimage.length){
35567             cls += ' roo-brick-center-title';
35568         }
35569         
35570         if(!this.html.length && this.bgimage.length){
35571             cls += ' roo-brick-bottom-title';
35572         }
35573         
35574         if(this.cls){
35575             cls += ' ' + this.cls;
35576         }
35577         
35578         var cfg = {
35579             tag: (this.href.length) ? 'a' : 'div',
35580             cls: cls,
35581             cn: [
35582                 {
35583                     tag: 'div',
35584                     cls: 'roo-brick-paragraph',
35585                     cn: []
35586                 }
35587             ]
35588         };
35589         
35590         if(this.href.length){
35591             cfg.href = this.href;
35592         }
35593         
35594         var cn = cfg.cn[0].cn;
35595         
35596         if(this.title.length){
35597             cn.push({
35598                 tag: 'h4',
35599                 cls: 'roo-brick-title',
35600                 html: this.title
35601             });
35602         }
35603         
35604         if(this.html.length){
35605             cn.push({
35606                 tag: 'p',
35607                 cls: 'roo-brick-text',
35608                 html: this.html
35609             });
35610         } else {
35611             cn.cls += ' hide';
35612         }
35613         
35614         if(this.bgimage.length){
35615             cfg.cn.push({
35616                 tag: 'img',
35617                 cls: 'roo-brick-image-view',
35618                 src: this.bgimage
35619             });
35620         }
35621         
35622         return cfg;
35623     },
35624     
35625     initEvents: function() 
35626     {
35627         if(this.title.length || this.html.length){
35628             this.el.on('mouseenter'  ,this.enter, this);
35629             this.el.on('mouseleave', this.leave, this);
35630         }
35631         
35632         Roo.EventManager.onWindowResize(this.resize, this); 
35633         
35634         if(this.bgimage.length){
35635             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35636             this.imageEl.on('load', this.onImageLoad, this);
35637             return;
35638         }
35639         
35640         this.resize();
35641     },
35642     
35643     onImageLoad : function()
35644     {
35645         this.resize();
35646     },
35647     
35648     resize : function()
35649     {
35650         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35651         
35652         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35653         
35654         if(this.bgimage.length){
35655             var image = this.el.select('.roo-brick-image-view', true).first();
35656             
35657             image.setWidth(paragraph.getWidth());
35658             
35659             if(this.square){
35660                 image.setHeight(paragraph.getWidth());
35661             }
35662             
35663             this.el.setHeight(image.getHeight());
35664             paragraph.setHeight(image.getHeight());
35665             
35666         }
35667         
35668     },
35669     
35670     enter: function(e, el)
35671     {
35672         e.preventDefault();
35673         
35674         if(this.bgimage.length){
35675             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35676             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35677         }
35678     },
35679     
35680     leave: function(e, el)
35681     {
35682         e.preventDefault();
35683         
35684         if(this.bgimage.length){
35685             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35686             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35687         }
35688     }
35689     
35690 });
35691
35692  
35693
35694  /*
35695  * - LGPL
35696  *
35697  * Number field 
35698  */
35699
35700 /**
35701  * @class Roo.bootstrap.NumberField
35702  * @extends Roo.bootstrap.Input
35703  * Bootstrap NumberField class
35704  * 
35705  * 
35706  * 
35707  * 
35708  * @constructor
35709  * Create a new NumberField
35710  * @param {Object} config The config object
35711  */
35712
35713 Roo.bootstrap.NumberField = function(config){
35714     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35715 };
35716
35717 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35718     
35719     /**
35720      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35721      */
35722     allowDecimals : true,
35723     /**
35724      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35725      */
35726     decimalSeparator : ".",
35727     /**
35728      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35729      */
35730     decimalPrecision : 2,
35731     /**
35732      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35733      */
35734     allowNegative : true,
35735     
35736     /**
35737      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35738      */
35739     allowZero: true,
35740     /**
35741      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35742      */
35743     minValue : Number.NEGATIVE_INFINITY,
35744     /**
35745      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35746      */
35747     maxValue : Number.MAX_VALUE,
35748     /**
35749      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35750      */
35751     minText : "The minimum value for this field is {0}",
35752     /**
35753      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35754      */
35755     maxText : "The maximum value for this field is {0}",
35756     /**
35757      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
35758      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35759      */
35760     nanText : "{0} is not a valid number",
35761     /**
35762      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35763      */
35764     thousandsDelimiter : false,
35765     /**
35766      * @cfg {String} valueAlign alignment of value
35767      */
35768     valueAlign : "left",
35769
35770     getAutoCreate : function()
35771     {
35772         var hiddenInput = {
35773             tag: 'input',
35774             type: 'hidden',
35775             id: Roo.id(),
35776             cls: 'hidden-number-input'
35777         };
35778         
35779         if (this.name) {
35780             hiddenInput.name = this.name;
35781         }
35782         
35783         this.name = '';
35784         
35785         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35786         
35787         this.name = hiddenInput.name;
35788         
35789         if(cfg.cn.length > 0) {
35790             cfg.cn.push(hiddenInput);
35791         }
35792         
35793         return cfg;
35794     },
35795
35796     // private
35797     initEvents : function()
35798     {   
35799         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35800         
35801         var allowed = "0123456789";
35802         
35803         if(this.allowDecimals){
35804             allowed += this.decimalSeparator;
35805         }
35806         
35807         if(this.allowNegative){
35808             allowed += "-";
35809         }
35810         
35811         if(this.thousandsDelimiter) {
35812             allowed += ",";
35813         }
35814         
35815         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
35816         
35817         var keyPress = function(e){
35818             
35819             var k = e.getKey();
35820             
35821             var c = e.getCharCode();
35822             
35823             if(
35824                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
35825                     allowed.indexOf(String.fromCharCode(c)) === -1
35826             ){
35827                 e.stopEvent();
35828                 return;
35829             }
35830             
35831             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
35832                 return;
35833             }
35834             
35835             if(allowed.indexOf(String.fromCharCode(c)) === -1){
35836                 e.stopEvent();
35837             }
35838         };
35839         
35840         this.el.on("keypress", keyPress, this);
35841     },
35842     
35843     validateValue : function(value)
35844     {
35845         
35846         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
35847             return false;
35848         }
35849         
35850         var num = this.parseValue(value);
35851         
35852         if(isNaN(num)){
35853             this.markInvalid(String.format(this.nanText, value));
35854             return false;
35855         }
35856         
35857         if(num < this.minValue){
35858             this.markInvalid(String.format(this.minText, this.minValue));
35859             return false;
35860         }
35861         
35862         if(num > this.maxValue){
35863             this.markInvalid(String.format(this.maxText, this.maxValue));
35864             return false;
35865         }
35866         
35867         return true;
35868     },
35869
35870     getValue : function()
35871     {
35872         var v = this.hiddenEl().getValue();
35873         
35874         return this.fixPrecision(this.parseValue(v));
35875     },
35876
35877     parseValue : function(value)
35878     {
35879         if(this.thousandsDelimiter) {
35880             value += "";
35881             r = new RegExp(",", "g");
35882             value = value.replace(r, "");
35883         }
35884         
35885         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
35886         return isNaN(value) ? '' : value;
35887     },
35888
35889     fixPrecision : function(value)
35890     {
35891         if(this.thousandsDelimiter) {
35892             value += "";
35893             r = new RegExp(",", "g");
35894             value = value.replace(r, "");
35895         }
35896         
35897         var nan = isNaN(value);
35898         
35899         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
35900             return nan ? '' : value;
35901         }
35902         return parseFloat(value).toFixed(this.decimalPrecision);
35903     },
35904
35905     setValue : function(v)
35906     {
35907         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
35908         
35909         this.value = v;
35910         
35911         if(this.rendered){
35912             
35913             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
35914             
35915             this.inputEl().dom.value = (v == '') ? '' :
35916                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
35917             
35918             if(!this.allowZero && v === '0') {
35919                 this.hiddenEl().dom.value = '';
35920                 this.inputEl().dom.value = '';
35921             }
35922             
35923             this.validate();
35924         }
35925     },
35926
35927     decimalPrecisionFcn : function(v)
35928     {
35929         return Math.floor(v);
35930     },
35931
35932     beforeBlur : function()
35933     {
35934         var v = this.parseValue(this.getRawValue());
35935         
35936         if(v || v === 0 || v === ''){
35937             this.setValue(v);
35938         }
35939     },
35940     
35941     hiddenEl : function()
35942     {
35943         return this.el.select('input.hidden-number-input',true).first();
35944     }
35945     
35946 });
35947
35948  
35949
35950 /*
35951 * Licence: LGPL
35952 */
35953
35954 /**
35955  * @class Roo.bootstrap.DocumentSlider
35956  * @extends Roo.bootstrap.Component
35957  * Bootstrap DocumentSlider class
35958  * 
35959  * @constructor
35960  * Create a new DocumentViewer
35961  * @param {Object} config The config object
35962  */
35963
35964 Roo.bootstrap.DocumentSlider = function(config){
35965     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
35966     
35967     this.files = [];
35968     
35969     this.addEvents({
35970         /**
35971          * @event initial
35972          * Fire after initEvent
35973          * @param {Roo.bootstrap.DocumentSlider} this
35974          */
35975         "initial" : true,
35976         /**
35977          * @event update
35978          * Fire after update
35979          * @param {Roo.bootstrap.DocumentSlider} this
35980          */
35981         "update" : true,
35982         /**
35983          * @event click
35984          * Fire after click
35985          * @param {Roo.bootstrap.DocumentSlider} this
35986          */
35987         "click" : true
35988     });
35989 };
35990
35991 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
35992     
35993     files : false,
35994     
35995     indicator : 0,
35996     
35997     getAutoCreate : function()
35998     {
35999         var cfg = {
36000             tag : 'div',
36001             cls : 'roo-document-slider',
36002             cn : [
36003                 {
36004                     tag : 'div',
36005                     cls : 'roo-document-slider-header',
36006                     cn : [
36007                         {
36008                             tag : 'div',
36009                             cls : 'roo-document-slider-header-title'
36010                         }
36011                     ]
36012                 },
36013                 {
36014                     tag : 'div',
36015                     cls : 'roo-document-slider-body',
36016                     cn : [
36017                         {
36018                             tag : 'div',
36019                             cls : 'roo-document-slider-prev',
36020                             cn : [
36021                                 {
36022                                     tag : 'i',
36023                                     cls : 'fa fa-chevron-left'
36024                                 }
36025                             ]
36026                         },
36027                         {
36028                             tag : 'div',
36029                             cls : 'roo-document-slider-thumb',
36030                             cn : [
36031                                 {
36032                                     tag : 'img',
36033                                     cls : 'roo-document-slider-image'
36034                                 }
36035                             ]
36036                         },
36037                         {
36038                             tag : 'div',
36039                             cls : 'roo-document-slider-next',
36040                             cn : [
36041                                 {
36042                                     tag : 'i',
36043                                     cls : 'fa fa-chevron-right'
36044                                 }
36045                             ]
36046                         }
36047                     ]
36048                 }
36049             ]
36050         };
36051         
36052         return cfg;
36053     },
36054     
36055     initEvents : function()
36056     {
36057         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36058         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36059         
36060         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36061         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36062         
36063         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36064         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36065         
36066         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36067         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36068         
36069         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36070         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36071         
36072         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36073         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36074         
36075         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36076         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36077         
36078         this.thumbEl.on('click', this.onClick, this);
36079         
36080         this.prevIndicator.on('click', this.prev, this);
36081         
36082         this.nextIndicator.on('click', this.next, this);
36083         
36084     },
36085     
36086     initial : function()
36087     {
36088         if(this.files.length){
36089             this.indicator = 1;
36090             this.update()
36091         }
36092         
36093         this.fireEvent('initial', this);
36094     },
36095     
36096     update : function()
36097     {
36098         this.imageEl.attr('src', this.files[this.indicator - 1]);
36099         
36100         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36101         
36102         this.prevIndicator.show();
36103         
36104         if(this.indicator == 1){
36105             this.prevIndicator.hide();
36106         }
36107         
36108         this.nextIndicator.show();
36109         
36110         if(this.indicator == this.files.length){
36111             this.nextIndicator.hide();
36112         }
36113         
36114         this.thumbEl.scrollTo('top');
36115         
36116         this.fireEvent('update', this);
36117     },
36118     
36119     onClick : function(e)
36120     {
36121         e.preventDefault();
36122         
36123         this.fireEvent('click', this);
36124     },
36125     
36126     prev : function(e)
36127     {
36128         e.preventDefault();
36129         
36130         this.indicator = Math.max(1, this.indicator - 1);
36131         
36132         this.update();
36133     },
36134     
36135     next : function(e)
36136     {
36137         e.preventDefault();
36138         
36139         this.indicator = Math.min(this.files.length, this.indicator + 1);
36140         
36141         this.update();
36142     }
36143 });
36144 /*
36145  * - LGPL
36146  *
36147  * RadioSet
36148  *
36149  *
36150  */
36151
36152 /**
36153  * @class Roo.bootstrap.RadioSet
36154  * @extends Roo.bootstrap.Input
36155  * Bootstrap RadioSet class
36156  * @cfg {String} indicatorpos (left|right) default left
36157  * @cfg {Boolean} inline (true|false) inline the element (default true)
36158  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36159  * @constructor
36160  * Create a new RadioSet
36161  * @param {Object} config The config object
36162  */
36163
36164 Roo.bootstrap.RadioSet = function(config){
36165     
36166     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36167     
36168     this.radioes = [];
36169     
36170     Roo.bootstrap.RadioSet.register(this);
36171     
36172     this.addEvents({
36173         /**
36174         * @event check
36175         * Fires when the element is checked or unchecked.
36176         * @param {Roo.bootstrap.RadioSet} this This radio
36177         * @param {Roo.bootstrap.Radio} item The checked item
36178         */
36179        check : true,
36180        /**
36181         * @event click
36182         * Fires when the element is click.
36183         * @param {Roo.bootstrap.RadioSet} this This radio set
36184         * @param {Roo.bootstrap.Radio} item The checked item
36185         * @param {Roo.EventObject} e The event object
36186         */
36187        click : true
36188     });
36189     
36190 };
36191
36192 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36193
36194     radioes : false,
36195     
36196     inline : true,
36197     
36198     weight : '',
36199     
36200     indicatorpos : 'left',
36201     
36202     getAutoCreate : function()
36203     {
36204         var label = {
36205             tag : 'label',
36206             cls : 'roo-radio-set-label',
36207             cn : [
36208                 {
36209                     tag : 'span',
36210                     html : this.fieldLabel
36211                 }
36212             ]
36213         };
36214         if (Roo.bootstrap.version == 3) {
36215             
36216             
36217             if(this.indicatorpos == 'left'){
36218                 label.cn.unshift({
36219                     tag : 'i',
36220                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36221                     tooltip : 'This field is required'
36222                 });
36223             } else {
36224                 label.cn.push({
36225                     tag : 'i',
36226                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36227                     tooltip : 'This field is required'
36228                 });
36229             }
36230         }
36231         var items = {
36232             tag : 'div',
36233             cls : 'roo-radio-set-items'
36234         };
36235         
36236         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36237         
36238         if (align === 'left' && this.fieldLabel.length) {
36239             
36240             items = {
36241                 cls : "roo-radio-set-right", 
36242                 cn: [
36243                     items
36244                 ]
36245             };
36246             
36247             if(this.labelWidth > 12){
36248                 label.style = "width: " + this.labelWidth + 'px';
36249             }
36250             
36251             if(this.labelWidth < 13 && this.labelmd == 0){
36252                 this.labelmd = this.labelWidth;
36253             }
36254             
36255             if(this.labellg > 0){
36256                 label.cls += ' col-lg-' + this.labellg;
36257                 items.cls += ' col-lg-' + (12 - this.labellg);
36258             }
36259             
36260             if(this.labelmd > 0){
36261                 label.cls += ' col-md-' + this.labelmd;
36262                 items.cls += ' col-md-' + (12 - this.labelmd);
36263             }
36264             
36265             if(this.labelsm > 0){
36266                 label.cls += ' col-sm-' + this.labelsm;
36267                 items.cls += ' col-sm-' + (12 - this.labelsm);
36268             }
36269             
36270             if(this.labelxs > 0){
36271                 label.cls += ' col-xs-' + this.labelxs;
36272                 items.cls += ' col-xs-' + (12 - this.labelxs);
36273             }
36274         }
36275         
36276         var cfg = {
36277             tag : 'div',
36278             cls : 'roo-radio-set',
36279             cn : [
36280                 {
36281                     tag : 'input',
36282                     cls : 'roo-radio-set-input',
36283                     type : 'hidden',
36284                     name : this.name,
36285                     value : this.value ? this.value :  ''
36286                 },
36287                 label,
36288                 items
36289             ]
36290         };
36291         
36292         if(this.weight.length){
36293             cfg.cls += ' roo-radio-' + this.weight;
36294         }
36295         
36296         if(this.inline) {
36297             cfg.cls += ' roo-radio-set-inline';
36298         }
36299         
36300         var settings=this;
36301         ['xs','sm','md','lg'].map(function(size){
36302             if (settings[size]) {
36303                 cfg.cls += ' col-' + size + '-' + settings[size];
36304             }
36305         });
36306         
36307         return cfg;
36308         
36309     },
36310
36311     initEvents : function()
36312     {
36313         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36314         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36315         
36316         if(!this.fieldLabel.length){
36317             this.labelEl.hide();
36318         }
36319         
36320         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36321         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36322         
36323         this.indicator = this.indicatorEl();
36324         
36325         if(this.indicator){
36326             this.indicator.addClass('invisible');
36327         }
36328         
36329         this.originalValue = this.getValue();
36330         
36331     },
36332     
36333     inputEl: function ()
36334     {
36335         return this.el.select('.roo-radio-set-input', true).first();
36336     },
36337     
36338     getChildContainer : function()
36339     {
36340         return this.itemsEl;
36341     },
36342     
36343     register : function(item)
36344     {
36345         this.radioes.push(item);
36346         
36347     },
36348     
36349     validate : function()
36350     {   
36351         if(this.getVisibilityEl().hasClass('hidden')){
36352             return true;
36353         }
36354         
36355         var valid = false;
36356         
36357         Roo.each(this.radioes, function(i){
36358             if(!i.checked){
36359                 return;
36360             }
36361             
36362             valid = true;
36363             return false;
36364         });
36365         
36366         if(this.allowBlank) {
36367             return true;
36368         }
36369         
36370         if(this.disabled || valid){
36371             this.markValid();
36372             return true;
36373         }
36374         
36375         this.markInvalid();
36376         return false;
36377         
36378     },
36379     
36380     markValid : function()
36381     {
36382         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36383             this.indicatorEl().removeClass('visible');
36384             this.indicatorEl().addClass('invisible');
36385         }
36386         
36387         
36388         if (Roo.bootstrap.version == 3) {
36389             this.el.removeClass([this.invalidClass, this.validClass]);
36390             this.el.addClass(this.validClass);
36391         } else {
36392             this.el.removeClass(['is-invalid','is-valid']);
36393             this.el.addClass(['is-valid']);
36394         }
36395         this.fireEvent('valid', this);
36396     },
36397     
36398     markInvalid : function(msg)
36399     {
36400         if(this.allowBlank || this.disabled){
36401             return;
36402         }
36403         
36404         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36405             this.indicatorEl().removeClass('invisible');
36406             this.indicatorEl().addClass('visible');
36407         }
36408         if (Roo.bootstrap.version == 3) {
36409             this.el.removeClass([this.invalidClass, this.validClass]);
36410             this.el.addClass(this.invalidClass);
36411         } else {
36412             this.el.removeClass(['is-invalid','is-valid']);
36413             this.el.addClass(['is-invalid']);
36414         }
36415         
36416         this.fireEvent('invalid', this, msg);
36417         
36418     },
36419     
36420     setValue : function(v, suppressEvent)
36421     {   
36422         if(this.value === v){
36423             return;
36424         }
36425         
36426         this.value = v;
36427         
36428         if(this.rendered){
36429             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36430         }
36431         
36432         Roo.each(this.radioes, function(i){
36433             i.checked = false;
36434             i.el.removeClass('checked');
36435         });
36436         
36437         Roo.each(this.radioes, function(i){
36438             
36439             if(i.value === v || i.value.toString() === v.toString()){
36440                 i.checked = true;
36441                 i.el.addClass('checked');
36442                 
36443                 if(suppressEvent !== true){
36444                     this.fireEvent('check', this, i);
36445                 }
36446                 
36447                 return false;
36448             }
36449             
36450         }, this);
36451         
36452         this.validate();
36453     },
36454     
36455     clearInvalid : function(){
36456         
36457         if(!this.el || this.preventMark){
36458             return;
36459         }
36460         
36461         this.el.removeClass([this.invalidClass]);
36462         
36463         this.fireEvent('valid', this);
36464     }
36465     
36466 });
36467
36468 Roo.apply(Roo.bootstrap.RadioSet, {
36469     
36470     groups: {},
36471     
36472     register : function(set)
36473     {
36474         this.groups[set.name] = set;
36475     },
36476     
36477     get: function(name) 
36478     {
36479         if (typeof(this.groups[name]) == 'undefined') {
36480             return false;
36481         }
36482         
36483         return this.groups[name] ;
36484     }
36485     
36486 });
36487 /*
36488  * Based on:
36489  * Ext JS Library 1.1.1
36490  * Copyright(c) 2006-2007, Ext JS, LLC.
36491  *
36492  * Originally Released Under LGPL - original licence link has changed is not relivant.
36493  *
36494  * Fork - LGPL
36495  * <script type="text/javascript">
36496  */
36497
36498
36499 /**
36500  * @class Roo.bootstrap.SplitBar
36501  * @extends Roo.util.Observable
36502  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36503  * <br><br>
36504  * Usage:
36505  * <pre><code>
36506 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36507                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36508 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36509 split.minSize = 100;
36510 split.maxSize = 600;
36511 split.animate = true;
36512 split.on('moved', splitterMoved);
36513 </code></pre>
36514  * @constructor
36515  * Create a new SplitBar
36516  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36517  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36518  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36519  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36520                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36521                         position of the SplitBar).
36522  */
36523 Roo.bootstrap.SplitBar = function(cfg){
36524     
36525     /** @private */
36526     
36527     //{
36528     //  dragElement : elm
36529     //  resizingElement: el,
36530         // optional..
36531     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36532     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36533         // existingProxy ???
36534     //}
36535     
36536     this.el = Roo.get(cfg.dragElement, true);
36537     this.el.dom.unselectable = "on";
36538     /** @private */
36539     this.resizingEl = Roo.get(cfg.resizingElement, true);
36540
36541     /**
36542      * @private
36543      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36544      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36545      * @type Number
36546      */
36547     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36548     
36549     /**
36550      * The minimum size of the resizing element. (Defaults to 0)
36551      * @type Number
36552      */
36553     this.minSize = 0;
36554     
36555     /**
36556      * The maximum size of the resizing element. (Defaults to 2000)
36557      * @type Number
36558      */
36559     this.maxSize = 2000;
36560     
36561     /**
36562      * Whether to animate the transition to the new size
36563      * @type Boolean
36564      */
36565     this.animate = false;
36566     
36567     /**
36568      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36569      * @type Boolean
36570      */
36571     this.useShim = false;
36572     
36573     /** @private */
36574     this.shim = null;
36575     
36576     if(!cfg.existingProxy){
36577         /** @private */
36578         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36579     }else{
36580         this.proxy = Roo.get(cfg.existingProxy).dom;
36581     }
36582     /** @private */
36583     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36584     
36585     /** @private */
36586     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36587     
36588     /** @private */
36589     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36590     
36591     /** @private */
36592     this.dragSpecs = {};
36593     
36594     /**
36595      * @private The adapter to use to positon and resize elements
36596      */
36597     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36598     this.adapter.init(this);
36599     
36600     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36601         /** @private */
36602         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36603         this.el.addClass("roo-splitbar-h");
36604     }else{
36605         /** @private */
36606         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36607         this.el.addClass("roo-splitbar-v");
36608     }
36609     
36610     this.addEvents({
36611         /**
36612          * @event resize
36613          * Fires when the splitter is moved (alias for {@link #event-moved})
36614          * @param {Roo.bootstrap.SplitBar} this
36615          * @param {Number} newSize the new width or height
36616          */
36617         "resize" : true,
36618         /**
36619          * @event moved
36620          * Fires when the splitter is moved
36621          * @param {Roo.bootstrap.SplitBar} this
36622          * @param {Number} newSize the new width or height
36623          */
36624         "moved" : true,
36625         /**
36626          * @event beforeresize
36627          * Fires before the splitter is dragged
36628          * @param {Roo.bootstrap.SplitBar} this
36629          */
36630         "beforeresize" : true,
36631
36632         "beforeapply" : true
36633     });
36634
36635     Roo.util.Observable.call(this);
36636 };
36637
36638 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36639     onStartProxyDrag : function(x, y){
36640         this.fireEvent("beforeresize", this);
36641         if(!this.overlay){
36642             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36643             o.unselectable();
36644             o.enableDisplayMode("block");
36645             // all splitbars share the same overlay
36646             Roo.bootstrap.SplitBar.prototype.overlay = o;
36647         }
36648         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36649         this.overlay.show();
36650         Roo.get(this.proxy).setDisplayed("block");
36651         var size = this.adapter.getElementSize(this);
36652         this.activeMinSize = this.getMinimumSize();;
36653         this.activeMaxSize = this.getMaximumSize();;
36654         var c1 = size - this.activeMinSize;
36655         var c2 = Math.max(this.activeMaxSize - size, 0);
36656         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36657             this.dd.resetConstraints();
36658             this.dd.setXConstraint(
36659                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36660                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36661             );
36662             this.dd.setYConstraint(0, 0);
36663         }else{
36664             this.dd.resetConstraints();
36665             this.dd.setXConstraint(0, 0);
36666             this.dd.setYConstraint(
36667                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
36668                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36669             );
36670          }
36671         this.dragSpecs.startSize = size;
36672         this.dragSpecs.startPoint = [x, y];
36673         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36674     },
36675     
36676     /** 
36677      * @private Called after the drag operation by the DDProxy
36678      */
36679     onEndProxyDrag : function(e){
36680         Roo.get(this.proxy).setDisplayed(false);
36681         var endPoint = Roo.lib.Event.getXY(e);
36682         if(this.overlay){
36683             this.overlay.hide();
36684         }
36685         var newSize;
36686         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36687             newSize = this.dragSpecs.startSize + 
36688                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36689                     endPoint[0] - this.dragSpecs.startPoint[0] :
36690                     this.dragSpecs.startPoint[0] - endPoint[0]
36691                 );
36692         }else{
36693             newSize = this.dragSpecs.startSize + 
36694                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36695                     endPoint[1] - this.dragSpecs.startPoint[1] :
36696                     this.dragSpecs.startPoint[1] - endPoint[1]
36697                 );
36698         }
36699         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36700         if(newSize != this.dragSpecs.startSize){
36701             if(this.fireEvent('beforeapply', this, newSize) !== false){
36702                 this.adapter.setElementSize(this, newSize);
36703                 this.fireEvent("moved", this, newSize);
36704                 this.fireEvent("resize", this, newSize);
36705             }
36706         }
36707     },
36708     
36709     /**
36710      * Get the adapter this SplitBar uses
36711      * @return The adapter object
36712      */
36713     getAdapter : function(){
36714         return this.adapter;
36715     },
36716     
36717     /**
36718      * Set the adapter this SplitBar uses
36719      * @param {Object} adapter A SplitBar adapter object
36720      */
36721     setAdapter : function(adapter){
36722         this.adapter = adapter;
36723         this.adapter.init(this);
36724     },
36725     
36726     /**
36727      * Gets the minimum size for the resizing element
36728      * @return {Number} The minimum size
36729      */
36730     getMinimumSize : function(){
36731         return this.minSize;
36732     },
36733     
36734     /**
36735      * Sets the minimum size for the resizing element
36736      * @param {Number} minSize The minimum size
36737      */
36738     setMinimumSize : function(minSize){
36739         this.minSize = minSize;
36740     },
36741     
36742     /**
36743      * Gets the maximum size for the resizing element
36744      * @return {Number} The maximum size
36745      */
36746     getMaximumSize : function(){
36747         return this.maxSize;
36748     },
36749     
36750     /**
36751      * Sets the maximum size for the resizing element
36752      * @param {Number} maxSize The maximum size
36753      */
36754     setMaximumSize : function(maxSize){
36755         this.maxSize = maxSize;
36756     },
36757     
36758     /**
36759      * Sets the initialize size for the resizing element
36760      * @param {Number} size The initial size
36761      */
36762     setCurrentSize : function(size){
36763         var oldAnimate = this.animate;
36764         this.animate = false;
36765         this.adapter.setElementSize(this, size);
36766         this.animate = oldAnimate;
36767     },
36768     
36769     /**
36770      * Destroy this splitbar. 
36771      * @param {Boolean} removeEl True to remove the element
36772      */
36773     destroy : function(removeEl){
36774         if(this.shim){
36775             this.shim.remove();
36776         }
36777         this.dd.unreg();
36778         this.proxy.parentNode.removeChild(this.proxy);
36779         if(removeEl){
36780             this.el.remove();
36781         }
36782     }
36783 });
36784
36785 /**
36786  * @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.
36787  */
36788 Roo.bootstrap.SplitBar.createProxy = function(dir){
36789     var proxy = new Roo.Element(document.createElement("div"));
36790     proxy.unselectable();
36791     var cls = 'roo-splitbar-proxy';
36792     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36793     document.body.appendChild(proxy.dom);
36794     return proxy.dom;
36795 };
36796
36797 /** 
36798  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36799  * Default Adapter. It assumes the splitter and resizing element are not positioned
36800  * elements and only gets/sets the width of the element. Generally used for table based layouts.
36801  */
36802 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36803 };
36804
36805 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
36806     // do nothing for now
36807     init : function(s){
36808     
36809     },
36810     /**
36811      * Called before drag operations to get the current size of the resizing element. 
36812      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36813      */
36814      getElementSize : function(s){
36815         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36816             return s.resizingEl.getWidth();
36817         }else{
36818             return s.resizingEl.getHeight();
36819         }
36820     },
36821     
36822     /**
36823      * Called after drag operations to set the size of the resizing element.
36824      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36825      * @param {Number} newSize The new size to set
36826      * @param {Function} onComplete A function to be invoked when resizing is complete
36827      */
36828     setElementSize : function(s, newSize, onComplete){
36829         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36830             if(!s.animate){
36831                 s.resizingEl.setWidth(newSize);
36832                 if(onComplete){
36833                     onComplete(s, newSize);
36834                 }
36835             }else{
36836                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
36837             }
36838         }else{
36839             
36840             if(!s.animate){
36841                 s.resizingEl.setHeight(newSize);
36842                 if(onComplete){
36843                     onComplete(s, newSize);
36844                 }
36845             }else{
36846                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
36847             }
36848         }
36849     }
36850 };
36851
36852 /** 
36853  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
36854  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
36855  * Adapter that  moves the splitter element to align with the resized sizing element. 
36856  * Used with an absolute positioned SplitBar.
36857  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
36858  * document.body, make sure you assign an id to the body element.
36859  */
36860 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
36861     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36862     this.container = Roo.get(container);
36863 };
36864
36865 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
36866     init : function(s){
36867         this.basic.init(s);
36868     },
36869     
36870     getElementSize : function(s){
36871         return this.basic.getElementSize(s);
36872     },
36873     
36874     setElementSize : function(s, newSize, onComplete){
36875         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
36876     },
36877     
36878     moveSplitter : function(s){
36879         var yes = Roo.bootstrap.SplitBar;
36880         switch(s.placement){
36881             case yes.LEFT:
36882                 s.el.setX(s.resizingEl.getRight());
36883                 break;
36884             case yes.RIGHT:
36885                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
36886                 break;
36887             case yes.TOP:
36888                 s.el.setY(s.resizingEl.getBottom());
36889                 break;
36890             case yes.BOTTOM:
36891                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
36892                 break;
36893         }
36894     }
36895 };
36896
36897 /**
36898  * Orientation constant - Create a vertical SplitBar
36899  * @static
36900  * @type Number
36901  */
36902 Roo.bootstrap.SplitBar.VERTICAL = 1;
36903
36904 /**
36905  * Orientation constant - Create a horizontal SplitBar
36906  * @static
36907  * @type Number
36908  */
36909 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
36910
36911 /**
36912  * Placement constant - The resizing element is to the left of the splitter element
36913  * @static
36914  * @type Number
36915  */
36916 Roo.bootstrap.SplitBar.LEFT = 1;
36917
36918 /**
36919  * Placement constant - The resizing element is to the right of the splitter element
36920  * @static
36921  * @type Number
36922  */
36923 Roo.bootstrap.SplitBar.RIGHT = 2;
36924
36925 /**
36926  * Placement constant - The resizing element is positioned above the splitter element
36927  * @static
36928  * @type Number
36929  */
36930 Roo.bootstrap.SplitBar.TOP = 3;
36931
36932 /**
36933  * Placement constant - The resizing element is positioned under splitter element
36934  * @static
36935  * @type Number
36936  */
36937 Roo.bootstrap.SplitBar.BOTTOM = 4;
36938 Roo.namespace("Roo.bootstrap.layout");/*
36939  * Based on:
36940  * Ext JS Library 1.1.1
36941  * Copyright(c) 2006-2007, Ext JS, LLC.
36942  *
36943  * Originally Released Under LGPL - original licence link has changed is not relivant.
36944  *
36945  * Fork - LGPL
36946  * <script type="text/javascript">
36947  */
36948
36949 /**
36950  * @class Roo.bootstrap.layout.Manager
36951  * @extends Roo.bootstrap.Component
36952  * Base class for layout managers.
36953  */
36954 Roo.bootstrap.layout.Manager = function(config)
36955 {
36956     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
36957
36958
36959
36960
36961
36962     /** false to disable window resize monitoring @type Boolean */
36963     this.monitorWindowResize = true;
36964     this.regions = {};
36965     this.addEvents({
36966         /**
36967          * @event layout
36968          * Fires when a layout is performed.
36969          * @param {Roo.LayoutManager} this
36970          */
36971         "layout" : true,
36972         /**
36973          * @event regionresized
36974          * Fires when the user resizes a region.
36975          * @param {Roo.LayoutRegion} region The resized region
36976          * @param {Number} newSize The new size (width for east/west, height for north/south)
36977          */
36978         "regionresized" : true,
36979         /**
36980          * @event regioncollapsed
36981          * Fires when a region is collapsed.
36982          * @param {Roo.LayoutRegion} region The collapsed region
36983          */
36984         "regioncollapsed" : true,
36985         /**
36986          * @event regionexpanded
36987          * Fires when a region is expanded.
36988          * @param {Roo.LayoutRegion} region The expanded region
36989          */
36990         "regionexpanded" : true
36991     });
36992     this.updating = false;
36993
36994     if (config.el) {
36995         this.el = Roo.get(config.el);
36996         this.initEvents();
36997     }
36998
36999 };
37000
37001 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37002
37003
37004     regions : null,
37005
37006     monitorWindowResize : true,
37007
37008
37009     updating : false,
37010
37011
37012     onRender : function(ct, position)
37013     {
37014         if(!this.el){
37015             this.el = Roo.get(ct);
37016             this.initEvents();
37017         }
37018         //this.fireEvent('render',this);
37019     },
37020
37021
37022     initEvents: function()
37023     {
37024
37025
37026         // ie scrollbar fix
37027         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37028             document.body.scroll = "no";
37029         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37030             this.el.position('relative');
37031         }
37032         this.id = this.el.id;
37033         this.el.addClass("roo-layout-container");
37034         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37035         if(this.el.dom != document.body ) {
37036             this.el.on('resize', this.layout,this);
37037             this.el.on('show', this.layout,this);
37038         }
37039
37040     },
37041
37042     /**
37043      * Returns true if this layout is currently being updated
37044      * @return {Boolean}
37045      */
37046     isUpdating : function(){
37047         return this.updating;
37048     },
37049
37050     /**
37051      * Suspend the LayoutManager from doing auto-layouts while
37052      * making multiple add or remove calls
37053      */
37054     beginUpdate : function(){
37055         this.updating = true;
37056     },
37057
37058     /**
37059      * Restore auto-layouts and optionally disable the manager from performing a layout
37060      * @param {Boolean} noLayout true to disable a layout update
37061      */
37062     endUpdate : function(noLayout){
37063         this.updating = false;
37064         if(!noLayout){
37065             this.layout();
37066         }
37067     },
37068
37069     layout: function(){
37070         // abstract...
37071     },
37072
37073     onRegionResized : function(region, newSize){
37074         this.fireEvent("regionresized", region, newSize);
37075         this.layout();
37076     },
37077
37078     onRegionCollapsed : function(region){
37079         this.fireEvent("regioncollapsed", region);
37080     },
37081
37082     onRegionExpanded : function(region){
37083         this.fireEvent("regionexpanded", region);
37084     },
37085
37086     /**
37087      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37088      * performs box-model adjustments.
37089      * @return {Object} The size as an object {width: (the width), height: (the height)}
37090      */
37091     getViewSize : function()
37092     {
37093         var size;
37094         if(this.el.dom != document.body){
37095             size = this.el.getSize();
37096         }else{
37097             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37098         }
37099         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37100         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37101         return size;
37102     },
37103
37104     /**
37105      * Returns the Element this layout is bound to.
37106      * @return {Roo.Element}
37107      */
37108     getEl : function(){
37109         return this.el;
37110     },
37111
37112     /**
37113      * Returns the specified region.
37114      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37115      * @return {Roo.LayoutRegion}
37116      */
37117     getRegion : function(target){
37118         return this.regions[target.toLowerCase()];
37119     },
37120
37121     onWindowResize : function(){
37122         if(this.monitorWindowResize){
37123             this.layout();
37124         }
37125     }
37126 });
37127 /*
37128  * Based on:
37129  * Ext JS Library 1.1.1
37130  * Copyright(c) 2006-2007, Ext JS, LLC.
37131  *
37132  * Originally Released Under LGPL - original licence link has changed is not relivant.
37133  *
37134  * Fork - LGPL
37135  * <script type="text/javascript">
37136  */
37137 /**
37138  * @class Roo.bootstrap.layout.Border
37139  * @extends Roo.bootstrap.layout.Manager
37140  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37141  * please see: examples/bootstrap/nested.html<br><br>
37142  
37143 <b>The container the layout is rendered into can be either the body element or any other element.
37144 If it is not the body element, the container needs to either be an absolute positioned element,
37145 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37146 the container size if it is not the body element.</b>
37147
37148 * @constructor
37149 * Create a new Border
37150 * @param {Object} config Configuration options
37151  */
37152 Roo.bootstrap.layout.Border = function(config){
37153     config = config || {};
37154     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37155     
37156     
37157     
37158     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37159         if(config[region]){
37160             config[region].region = region;
37161             this.addRegion(config[region]);
37162         }
37163     },this);
37164     
37165 };
37166
37167 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
37168
37169 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37170     
37171     parent : false, // this might point to a 'nest' or a ???
37172     
37173     /**
37174      * Creates and adds a new region if it doesn't already exist.
37175      * @param {String} target The target region key (north, south, east, west or center).
37176      * @param {Object} config The regions config object
37177      * @return {BorderLayoutRegion} The new region
37178      */
37179     addRegion : function(config)
37180     {
37181         if(!this.regions[config.region]){
37182             var r = this.factory(config);
37183             this.bindRegion(r);
37184         }
37185         return this.regions[config.region];
37186     },
37187
37188     // private (kinda)
37189     bindRegion : function(r){
37190         this.regions[r.config.region] = r;
37191         
37192         r.on("visibilitychange",    this.layout, this);
37193         r.on("paneladded",          this.layout, this);
37194         r.on("panelremoved",        this.layout, this);
37195         r.on("invalidated",         this.layout, this);
37196         r.on("resized",             this.onRegionResized, this);
37197         r.on("collapsed",           this.onRegionCollapsed, this);
37198         r.on("expanded",            this.onRegionExpanded, this);
37199     },
37200
37201     /**
37202      * Performs a layout update.
37203      */
37204     layout : function()
37205     {
37206         if(this.updating) {
37207             return;
37208         }
37209         
37210         // render all the rebions if they have not been done alreayd?
37211         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37212             if(this.regions[region] && !this.regions[region].bodyEl){
37213                 this.regions[region].onRender(this.el)
37214             }
37215         },this);
37216         
37217         var size = this.getViewSize();
37218         var w = size.width;
37219         var h = size.height;
37220         var centerW = w;
37221         var centerH = h;
37222         var centerY = 0;
37223         var centerX = 0;
37224         //var x = 0, y = 0;
37225
37226         var rs = this.regions;
37227         var north = rs["north"];
37228         var south = rs["south"]; 
37229         var west = rs["west"];
37230         var east = rs["east"];
37231         var center = rs["center"];
37232         //if(this.hideOnLayout){ // not supported anymore
37233             //c.el.setStyle("display", "none");
37234         //}
37235         if(north && north.isVisible()){
37236             var b = north.getBox();
37237             var m = north.getMargins();
37238             b.width = w - (m.left+m.right);
37239             b.x = m.left;
37240             b.y = m.top;
37241             centerY = b.height + b.y + m.bottom;
37242             centerH -= centerY;
37243             north.updateBox(this.safeBox(b));
37244         }
37245         if(south && south.isVisible()){
37246             var b = south.getBox();
37247             var m = south.getMargins();
37248             b.width = w - (m.left+m.right);
37249             b.x = m.left;
37250             var totalHeight = (b.height + m.top + m.bottom);
37251             b.y = h - totalHeight + m.top;
37252             centerH -= totalHeight;
37253             south.updateBox(this.safeBox(b));
37254         }
37255         if(west && west.isVisible()){
37256             var b = west.getBox();
37257             var m = west.getMargins();
37258             b.height = centerH - (m.top+m.bottom);
37259             b.x = m.left;
37260             b.y = centerY + m.top;
37261             var totalWidth = (b.width + m.left + m.right);
37262             centerX += totalWidth;
37263             centerW -= totalWidth;
37264             west.updateBox(this.safeBox(b));
37265         }
37266         if(east && east.isVisible()){
37267             var b = east.getBox();
37268             var m = east.getMargins();
37269             b.height = centerH - (m.top+m.bottom);
37270             var totalWidth = (b.width + m.left + m.right);
37271             b.x = w - totalWidth + m.left;
37272             b.y = centerY + m.top;
37273             centerW -= totalWidth;
37274             east.updateBox(this.safeBox(b));
37275         }
37276         if(center){
37277             var m = center.getMargins();
37278             var centerBox = {
37279                 x: centerX + m.left,
37280                 y: centerY + m.top,
37281                 width: centerW - (m.left+m.right),
37282                 height: centerH - (m.top+m.bottom)
37283             };
37284             //if(this.hideOnLayout){
37285                 //center.el.setStyle("display", "block");
37286             //}
37287             center.updateBox(this.safeBox(centerBox));
37288         }
37289         this.el.repaint();
37290         this.fireEvent("layout", this);
37291     },
37292
37293     // private
37294     safeBox : function(box){
37295         box.width = Math.max(0, box.width);
37296         box.height = Math.max(0, box.height);
37297         return box;
37298     },
37299
37300     /**
37301      * Adds a ContentPanel (or subclass) to this layout.
37302      * @param {String} target The target region key (north, south, east, west or center).
37303      * @param {Roo.ContentPanel} panel The panel to add
37304      * @return {Roo.ContentPanel} The added panel
37305      */
37306     add : function(target, panel){
37307          
37308         target = target.toLowerCase();
37309         return this.regions[target].add(panel);
37310     },
37311
37312     /**
37313      * Remove a ContentPanel (or subclass) to this layout.
37314      * @param {String} target The target region key (north, south, east, west or center).
37315      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37316      * @return {Roo.ContentPanel} The removed panel
37317      */
37318     remove : function(target, panel){
37319         target = target.toLowerCase();
37320         return this.regions[target].remove(panel);
37321     },
37322
37323     /**
37324      * Searches all regions for a panel with the specified id
37325      * @param {String} panelId
37326      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37327      */
37328     findPanel : function(panelId){
37329         var rs = this.regions;
37330         for(var target in rs){
37331             if(typeof rs[target] != "function"){
37332                 var p = rs[target].getPanel(panelId);
37333                 if(p){
37334                     return p;
37335                 }
37336             }
37337         }
37338         return null;
37339     },
37340
37341     /**
37342      * Searches all regions for a panel with the specified id and activates (shows) it.
37343      * @param {String/ContentPanel} panelId The panels id or the panel itself
37344      * @return {Roo.ContentPanel} The shown panel or null
37345      */
37346     showPanel : function(panelId) {
37347       var rs = this.regions;
37348       for(var target in rs){
37349          var r = rs[target];
37350          if(typeof r != "function"){
37351             if(r.hasPanel(panelId)){
37352                return r.showPanel(panelId);
37353             }
37354          }
37355       }
37356       return null;
37357    },
37358
37359    /**
37360      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37361      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37362      */
37363    /*
37364     restoreState : function(provider){
37365         if(!provider){
37366             provider = Roo.state.Manager;
37367         }
37368         var sm = new Roo.LayoutStateManager();
37369         sm.init(this, provider);
37370     },
37371 */
37372  
37373  
37374     /**
37375      * Adds a xtype elements to the layout.
37376      * <pre><code>
37377
37378 layout.addxtype({
37379        xtype : 'ContentPanel',
37380        region: 'west',
37381        items: [ .... ]
37382    }
37383 );
37384
37385 layout.addxtype({
37386         xtype : 'NestedLayoutPanel',
37387         region: 'west',
37388         layout: {
37389            center: { },
37390            west: { }   
37391         },
37392         items : [ ... list of content panels or nested layout panels.. ]
37393    }
37394 );
37395 </code></pre>
37396      * @param {Object} cfg Xtype definition of item to add.
37397      */
37398     addxtype : function(cfg)
37399     {
37400         // basically accepts a pannel...
37401         // can accept a layout region..!?!?
37402         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37403         
37404         
37405         // theory?  children can only be panels??
37406         
37407         //if (!cfg.xtype.match(/Panel$/)) {
37408         //    return false;
37409         //}
37410         var ret = false;
37411         
37412         if (typeof(cfg.region) == 'undefined') {
37413             Roo.log("Failed to add Panel, region was not set");
37414             Roo.log(cfg);
37415             return false;
37416         }
37417         var region = cfg.region;
37418         delete cfg.region;
37419         
37420           
37421         var xitems = [];
37422         if (cfg.items) {
37423             xitems = cfg.items;
37424             delete cfg.items;
37425         }
37426         var nb = false;
37427         
37428         if ( region == 'center') {
37429             Roo.log("Center: " + cfg.title);
37430         }
37431         
37432         
37433         switch(cfg.xtype) 
37434         {
37435             case 'Content':  // ContentPanel (el, cfg)
37436             case 'Scroll':  // ContentPanel (el, cfg)
37437             case 'View': 
37438                 cfg.autoCreate = cfg.autoCreate || true;
37439                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37440                 //} else {
37441                 //    var el = this.el.createChild();
37442                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37443                 //}
37444                 
37445                 this.add(region, ret);
37446                 break;
37447             
37448             /*
37449             case 'TreePanel': // our new panel!
37450                 cfg.el = this.el.createChild();
37451                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37452                 this.add(region, ret);
37453                 break;
37454             */
37455             
37456             case 'Nest': 
37457                 // create a new Layout (which is  a Border Layout...
37458                 
37459                 var clayout = cfg.layout;
37460                 clayout.el  = this.el.createChild();
37461                 clayout.items   = clayout.items  || [];
37462                 
37463                 delete cfg.layout;
37464                 
37465                 // replace this exitems with the clayout ones..
37466                 xitems = clayout.items;
37467                  
37468                 // force background off if it's in center...
37469                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37470                     cfg.background = false;
37471                 }
37472                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37473                 
37474                 
37475                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37476                 //console.log('adding nested layout panel '  + cfg.toSource());
37477                 this.add(region, ret);
37478                 nb = {}; /// find first...
37479                 break;
37480             
37481             case 'Grid':
37482                 
37483                 // needs grid and region
37484                 
37485                 //var el = this.getRegion(region).el.createChild();
37486                 /*
37487                  *var el = this.el.createChild();
37488                 // create the grid first...
37489                 cfg.grid.container = el;
37490                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37491                 */
37492                 
37493                 if (region == 'center' && this.active ) {
37494                     cfg.background = false;
37495                 }
37496                 
37497                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37498                 
37499                 this.add(region, ret);
37500                 /*
37501                 if (cfg.background) {
37502                     // render grid on panel activation (if panel background)
37503                     ret.on('activate', function(gp) {
37504                         if (!gp.grid.rendered) {
37505                     //        gp.grid.render(el);
37506                         }
37507                     });
37508                 } else {
37509                   //  cfg.grid.render(el);
37510                 }
37511                 */
37512                 break;
37513            
37514            
37515             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37516                 // it was the old xcomponent building that caused this before.
37517                 // espeically if border is the top element in the tree.
37518                 ret = this;
37519                 break; 
37520                 
37521                     
37522                 
37523                 
37524                 
37525             default:
37526                 /*
37527                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37528                     
37529                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37530                     this.add(region, ret);
37531                 } else {
37532                 */
37533                     Roo.log(cfg);
37534                     throw "Can not add '" + cfg.xtype + "' to Border";
37535                     return null;
37536              
37537                                 
37538              
37539         }
37540         this.beginUpdate();
37541         // add children..
37542         var region = '';
37543         var abn = {};
37544         Roo.each(xitems, function(i)  {
37545             region = nb && i.region ? i.region : false;
37546             
37547             var add = ret.addxtype(i);
37548            
37549             if (region) {
37550                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37551                 if (!i.background) {
37552                     abn[region] = nb[region] ;
37553                 }
37554             }
37555             
37556         });
37557         this.endUpdate();
37558
37559         // make the last non-background panel active..
37560         //if (nb) { Roo.log(abn); }
37561         if (nb) {
37562             
37563             for(var r in abn) {
37564                 region = this.getRegion(r);
37565                 if (region) {
37566                     // tried using nb[r], but it does not work..
37567                      
37568                     region.showPanel(abn[r]);
37569                    
37570                 }
37571             }
37572         }
37573         return ret;
37574         
37575     },
37576     
37577     
37578 // private
37579     factory : function(cfg)
37580     {
37581         
37582         var validRegions = Roo.bootstrap.layout.Border.regions;
37583
37584         var target = cfg.region;
37585         cfg.mgr = this;
37586         
37587         var r = Roo.bootstrap.layout;
37588         Roo.log(target);
37589         switch(target){
37590             case "north":
37591                 return new r.North(cfg);
37592             case "south":
37593                 return new r.South(cfg);
37594             case "east":
37595                 return new r.East(cfg);
37596             case "west":
37597                 return new r.West(cfg);
37598             case "center":
37599                 return new r.Center(cfg);
37600         }
37601         throw 'Layout region "'+target+'" not supported.';
37602     }
37603     
37604     
37605 });
37606  /*
37607  * Based on:
37608  * Ext JS Library 1.1.1
37609  * Copyright(c) 2006-2007, Ext JS, LLC.
37610  *
37611  * Originally Released Under LGPL - original licence link has changed is not relivant.
37612  *
37613  * Fork - LGPL
37614  * <script type="text/javascript">
37615  */
37616  
37617 /**
37618  * @class Roo.bootstrap.layout.Basic
37619  * @extends Roo.util.Observable
37620  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37621  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37622  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37623  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37624  * @cfg {string}   region  the region that it inhabits..
37625  * @cfg {bool}   skipConfig skip config?
37626  * 
37627
37628  */
37629 Roo.bootstrap.layout.Basic = function(config){
37630     
37631     this.mgr = config.mgr;
37632     
37633     this.position = config.region;
37634     
37635     var skipConfig = config.skipConfig;
37636     
37637     this.events = {
37638         /**
37639          * @scope Roo.BasicLayoutRegion
37640          */
37641         
37642         /**
37643          * @event beforeremove
37644          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37645          * @param {Roo.LayoutRegion} this
37646          * @param {Roo.ContentPanel} panel The panel
37647          * @param {Object} e The cancel event object
37648          */
37649         "beforeremove" : true,
37650         /**
37651          * @event invalidated
37652          * Fires when the layout for this region is changed.
37653          * @param {Roo.LayoutRegion} this
37654          */
37655         "invalidated" : true,
37656         /**
37657          * @event visibilitychange
37658          * Fires when this region is shown or hidden 
37659          * @param {Roo.LayoutRegion} this
37660          * @param {Boolean} visibility true or false
37661          */
37662         "visibilitychange" : true,
37663         /**
37664          * @event paneladded
37665          * Fires when a panel is added. 
37666          * @param {Roo.LayoutRegion} this
37667          * @param {Roo.ContentPanel} panel The panel
37668          */
37669         "paneladded" : true,
37670         /**
37671          * @event panelremoved
37672          * Fires when a panel is removed. 
37673          * @param {Roo.LayoutRegion} this
37674          * @param {Roo.ContentPanel} panel The panel
37675          */
37676         "panelremoved" : true,
37677         /**
37678          * @event beforecollapse
37679          * Fires when this region before collapse.
37680          * @param {Roo.LayoutRegion} this
37681          */
37682         "beforecollapse" : true,
37683         /**
37684          * @event collapsed
37685          * Fires when this region is collapsed.
37686          * @param {Roo.LayoutRegion} this
37687          */
37688         "collapsed" : true,
37689         /**
37690          * @event expanded
37691          * Fires when this region is expanded.
37692          * @param {Roo.LayoutRegion} this
37693          */
37694         "expanded" : true,
37695         /**
37696          * @event slideshow
37697          * Fires when this region is slid into view.
37698          * @param {Roo.LayoutRegion} this
37699          */
37700         "slideshow" : true,
37701         /**
37702          * @event slidehide
37703          * Fires when this region slides out of view. 
37704          * @param {Roo.LayoutRegion} this
37705          */
37706         "slidehide" : true,
37707         /**
37708          * @event panelactivated
37709          * Fires when a panel is activated. 
37710          * @param {Roo.LayoutRegion} this
37711          * @param {Roo.ContentPanel} panel The activated panel
37712          */
37713         "panelactivated" : true,
37714         /**
37715          * @event resized
37716          * Fires when the user resizes this region. 
37717          * @param {Roo.LayoutRegion} this
37718          * @param {Number} newSize The new size (width for east/west, height for north/south)
37719          */
37720         "resized" : true
37721     };
37722     /** A collection of panels in this region. @type Roo.util.MixedCollection */
37723     this.panels = new Roo.util.MixedCollection();
37724     this.panels.getKey = this.getPanelId.createDelegate(this);
37725     this.box = null;
37726     this.activePanel = null;
37727     // ensure listeners are added...
37728     
37729     if (config.listeners || config.events) {
37730         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37731             listeners : config.listeners || {},
37732             events : config.events || {}
37733         });
37734     }
37735     
37736     if(skipConfig !== true){
37737         this.applyConfig(config);
37738     }
37739 };
37740
37741 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37742 {
37743     getPanelId : function(p){
37744         return p.getId();
37745     },
37746     
37747     applyConfig : function(config){
37748         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37749         this.config = config;
37750         
37751     },
37752     
37753     /**
37754      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
37755      * the width, for horizontal (north, south) the height.
37756      * @param {Number} newSize The new width or height
37757      */
37758     resizeTo : function(newSize){
37759         var el = this.el ? this.el :
37760                  (this.activePanel ? this.activePanel.getEl() : null);
37761         if(el){
37762             switch(this.position){
37763                 case "east":
37764                 case "west":
37765                     el.setWidth(newSize);
37766                     this.fireEvent("resized", this, newSize);
37767                 break;
37768                 case "north":
37769                 case "south":
37770                     el.setHeight(newSize);
37771                     this.fireEvent("resized", this, newSize);
37772                 break;                
37773             }
37774         }
37775     },
37776     
37777     getBox : function(){
37778         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37779     },
37780     
37781     getMargins : function(){
37782         return this.margins;
37783     },
37784     
37785     updateBox : function(box){
37786         this.box = box;
37787         var el = this.activePanel.getEl();
37788         el.dom.style.left = box.x + "px";
37789         el.dom.style.top = box.y + "px";
37790         this.activePanel.setSize(box.width, box.height);
37791     },
37792     
37793     /**
37794      * Returns the container element for this region.
37795      * @return {Roo.Element}
37796      */
37797     getEl : function(){
37798         return this.activePanel;
37799     },
37800     
37801     /**
37802      * Returns true if this region is currently visible.
37803      * @return {Boolean}
37804      */
37805     isVisible : function(){
37806         return this.activePanel ? true : false;
37807     },
37808     
37809     setActivePanel : function(panel){
37810         panel = this.getPanel(panel);
37811         if(this.activePanel && this.activePanel != panel){
37812             this.activePanel.setActiveState(false);
37813             this.activePanel.getEl().setLeftTop(-10000,-10000);
37814         }
37815         this.activePanel = panel;
37816         panel.setActiveState(true);
37817         if(this.box){
37818             panel.setSize(this.box.width, this.box.height);
37819         }
37820         this.fireEvent("panelactivated", this, panel);
37821         this.fireEvent("invalidated");
37822     },
37823     
37824     /**
37825      * Show the specified panel.
37826      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
37827      * @return {Roo.ContentPanel} The shown panel or null
37828      */
37829     showPanel : function(panel){
37830         panel = this.getPanel(panel);
37831         if(panel){
37832             this.setActivePanel(panel);
37833         }
37834         return panel;
37835     },
37836     
37837     /**
37838      * Get the active panel for this region.
37839      * @return {Roo.ContentPanel} The active panel or null
37840      */
37841     getActivePanel : function(){
37842         return this.activePanel;
37843     },
37844     
37845     /**
37846      * Add the passed ContentPanel(s)
37847      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37848      * @return {Roo.ContentPanel} The panel added (if only one was added)
37849      */
37850     add : function(panel){
37851         if(arguments.length > 1){
37852             for(var i = 0, len = arguments.length; i < len; i++) {
37853                 this.add(arguments[i]);
37854             }
37855             return null;
37856         }
37857         if(this.hasPanel(panel)){
37858             this.showPanel(panel);
37859             return panel;
37860         }
37861         var el = panel.getEl();
37862         if(el.dom.parentNode != this.mgr.el.dom){
37863             this.mgr.el.dom.appendChild(el.dom);
37864         }
37865         if(panel.setRegion){
37866             panel.setRegion(this);
37867         }
37868         this.panels.add(panel);
37869         el.setStyle("position", "absolute");
37870         if(!panel.background){
37871             this.setActivePanel(panel);
37872             if(this.config.initialSize && this.panels.getCount()==1){
37873                 this.resizeTo(this.config.initialSize);
37874             }
37875         }
37876         this.fireEvent("paneladded", this, panel);
37877         return panel;
37878     },
37879     
37880     /**
37881      * Returns true if the panel is in this region.
37882      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37883      * @return {Boolean}
37884      */
37885     hasPanel : function(panel){
37886         if(typeof panel == "object"){ // must be panel obj
37887             panel = panel.getId();
37888         }
37889         return this.getPanel(panel) ? true : false;
37890     },
37891     
37892     /**
37893      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37894      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37895      * @param {Boolean} preservePanel Overrides the config preservePanel option
37896      * @return {Roo.ContentPanel} The panel that was removed
37897      */
37898     remove : function(panel, preservePanel){
37899         panel = this.getPanel(panel);
37900         if(!panel){
37901             return null;
37902         }
37903         var e = {};
37904         this.fireEvent("beforeremove", this, panel, e);
37905         if(e.cancel === true){
37906             return null;
37907         }
37908         var panelId = panel.getId();
37909         this.panels.removeKey(panelId);
37910         return panel;
37911     },
37912     
37913     /**
37914      * Returns the panel specified or null if it's not in this region.
37915      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37916      * @return {Roo.ContentPanel}
37917      */
37918     getPanel : function(id){
37919         if(typeof id == "object"){ // must be panel obj
37920             return id;
37921         }
37922         return this.panels.get(id);
37923     },
37924     
37925     /**
37926      * Returns this regions position (north/south/east/west/center).
37927      * @return {String} 
37928      */
37929     getPosition: function(){
37930         return this.position;    
37931     }
37932 });/*
37933  * Based on:
37934  * Ext JS Library 1.1.1
37935  * Copyright(c) 2006-2007, Ext JS, LLC.
37936  *
37937  * Originally Released Under LGPL - original licence link has changed is not relivant.
37938  *
37939  * Fork - LGPL
37940  * <script type="text/javascript">
37941  */
37942  
37943 /**
37944  * @class Roo.bootstrap.layout.Region
37945  * @extends Roo.bootstrap.layout.Basic
37946  * This class represents a region in a layout manager.
37947  
37948  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
37949  * @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})
37950  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
37951  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
37952  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
37953  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
37954  * @cfg {String}    title           The title for the region (overrides panel titles)
37955  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
37956  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
37957  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
37958  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
37959  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
37960  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
37961  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
37962  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
37963  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
37964  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
37965
37966  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
37967  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
37968  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
37969  * @cfg {Number}    width           For East/West panels
37970  * @cfg {Number}    height          For North/South panels
37971  * @cfg {Boolean}   split           To show the splitter
37972  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
37973  * 
37974  * @cfg {string}   cls             Extra CSS classes to add to region
37975  * 
37976  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37977  * @cfg {string}   region  the region that it inhabits..
37978  *
37979
37980  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
37981  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
37982
37983  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
37984  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
37985  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
37986  */
37987 Roo.bootstrap.layout.Region = function(config)
37988 {
37989     this.applyConfig(config);
37990
37991     var mgr = config.mgr;
37992     var pos = config.region;
37993     config.skipConfig = true;
37994     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
37995     
37996     if (mgr.el) {
37997         this.onRender(mgr.el);   
37998     }
37999      
38000     this.visible = true;
38001     this.collapsed = false;
38002     this.unrendered_panels = [];
38003 };
38004
38005 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38006
38007     position: '', // set by wrapper (eg. north/south etc..)
38008     unrendered_panels : null,  // unrendered panels.
38009     
38010     tabPosition : false,
38011     
38012     mgr: false, // points to 'Border'
38013     
38014     
38015     createBody : function(){
38016         /** This region's body element 
38017         * @type Roo.Element */
38018         this.bodyEl = this.el.createChild({
38019                 tag: "div",
38020                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38021         });
38022     },
38023
38024     onRender: function(ctr, pos)
38025     {
38026         var dh = Roo.DomHelper;
38027         /** This region's container element 
38028         * @type Roo.Element */
38029         this.el = dh.append(ctr.dom, {
38030                 tag: "div",
38031                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38032             }, true);
38033         /** This region's title element 
38034         * @type Roo.Element */
38035     
38036         this.titleEl = dh.append(this.el.dom,  {
38037                 tag: "div",
38038                 unselectable: "on",
38039                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38040                 children:[
38041                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38042                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38043                 ]
38044             }, true);
38045         
38046         this.titleEl.enableDisplayMode();
38047         /** This region's title text element 
38048         * @type HTMLElement */
38049         this.titleTextEl = this.titleEl.dom.firstChild;
38050         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38051         /*
38052         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38053         this.closeBtn.enableDisplayMode();
38054         this.closeBtn.on("click", this.closeClicked, this);
38055         this.closeBtn.hide();
38056     */
38057         this.createBody(this.config);
38058         if(this.config.hideWhenEmpty){
38059             this.hide();
38060             this.on("paneladded", this.validateVisibility, this);
38061             this.on("panelremoved", this.validateVisibility, this);
38062         }
38063         if(this.autoScroll){
38064             this.bodyEl.setStyle("overflow", "auto");
38065         }else{
38066             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38067         }
38068         //if(c.titlebar !== false){
38069             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38070                 this.titleEl.hide();
38071             }else{
38072                 this.titleEl.show();
38073                 if(this.config.title){
38074                     this.titleTextEl.innerHTML = this.config.title;
38075                 }
38076             }
38077         //}
38078         if(this.config.collapsed){
38079             this.collapse(true);
38080         }
38081         if(this.config.hidden){
38082             this.hide();
38083         }
38084         
38085         if (this.unrendered_panels && this.unrendered_panels.length) {
38086             for (var i =0;i< this.unrendered_panels.length; i++) {
38087                 this.add(this.unrendered_panels[i]);
38088             }
38089             this.unrendered_panels = null;
38090             
38091         }
38092         
38093     },
38094     
38095     applyConfig : function(c)
38096     {
38097         /*
38098          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38099             var dh = Roo.DomHelper;
38100             if(c.titlebar !== false){
38101                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38102                 this.collapseBtn.on("click", this.collapse, this);
38103                 this.collapseBtn.enableDisplayMode();
38104                 /*
38105                 if(c.showPin === true || this.showPin){
38106                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38107                     this.stickBtn.enableDisplayMode();
38108                     this.stickBtn.on("click", this.expand, this);
38109                     this.stickBtn.hide();
38110                 }
38111                 
38112             }
38113             */
38114             /** This region's collapsed element
38115             * @type Roo.Element */
38116             /*
38117              *
38118             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38119                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38120             ]}, true);
38121             
38122             if(c.floatable !== false){
38123                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38124                this.collapsedEl.on("click", this.collapseClick, this);
38125             }
38126
38127             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38128                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38129                    id: "message", unselectable: "on", style:{"float":"left"}});
38130                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38131              }
38132             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38133             this.expandBtn.on("click", this.expand, this);
38134             
38135         }
38136         
38137         if(this.collapseBtn){
38138             this.collapseBtn.setVisible(c.collapsible == true);
38139         }
38140         
38141         this.cmargins = c.cmargins || this.cmargins ||
38142                          (this.position == "west" || this.position == "east" ?
38143                              {top: 0, left: 2, right:2, bottom: 0} :
38144                              {top: 2, left: 0, right:0, bottom: 2});
38145         */
38146         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38147         
38148         
38149         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38150         
38151         this.autoScroll = c.autoScroll || false;
38152         
38153         
38154        
38155         
38156         this.duration = c.duration || .30;
38157         this.slideDuration = c.slideDuration || .45;
38158         this.config = c;
38159        
38160     },
38161     /**
38162      * Returns true if this region is currently visible.
38163      * @return {Boolean}
38164      */
38165     isVisible : function(){
38166         return this.visible;
38167     },
38168
38169     /**
38170      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38171      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38172      */
38173     //setCollapsedTitle : function(title){
38174     //    title = title || "&#160;";
38175      //   if(this.collapsedTitleTextEl){
38176       //      this.collapsedTitleTextEl.innerHTML = title;
38177        // }
38178     //},
38179
38180     getBox : function(){
38181         var b;
38182       //  if(!this.collapsed){
38183             b = this.el.getBox(false, true);
38184        // }else{
38185           //  b = this.collapsedEl.getBox(false, true);
38186         //}
38187         return b;
38188     },
38189
38190     getMargins : function(){
38191         return this.margins;
38192         //return this.collapsed ? this.cmargins : this.margins;
38193     },
38194 /*
38195     highlight : function(){
38196         this.el.addClass("x-layout-panel-dragover");
38197     },
38198
38199     unhighlight : function(){
38200         this.el.removeClass("x-layout-panel-dragover");
38201     },
38202 */
38203     updateBox : function(box)
38204     {
38205         if (!this.bodyEl) {
38206             return; // not rendered yet..
38207         }
38208         
38209         this.box = box;
38210         if(!this.collapsed){
38211             this.el.dom.style.left = box.x + "px";
38212             this.el.dom.style.top = box.y + "px";
38213             this.updateBody(box.width, box.height);
38214         }else{
38215             this.collapsedEl.dom.style.left = box.x + "px";
38216             this.collapsedEl.dom.style.top = box.y + "px";
38217             this.collapsedEl.setSize(box.width, box.height);
38218         }
38219         if(this.tabs){
38220             this.tabs.autoSizeTabs();
38221         }
38222     },
38223
38224     updateBody : function(w, h)
38225     {
38226         if(w !== null){
38227             this.el.setWidth(w);
38228             w -= this.el.getBorderWidth("rl");
38229             if(this.config.adjustments){
38230                 w += this.config.adjustments[0];
38231             }
38232         }
38233         if(h !== null && h > 0){
38234             this.el.setHeight(h);
38235             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38236             h -= this.el.getBorderWidth("tb");
38237             if(this.config.adjustments){
38238                 h += this.config.adjustments[1];
38239             }
38240             this.bodyEl.setHeight(h);
38241             if(this.tabs){
38242                 h = this.tabs.syncHeight(h);
38243             }
38244         }
38245         if(this.panelSize){
38246             w = w !== null ? w : this.panelSize.width;
38247             h = h !== null ? h : this.panelSize.height;
38248         }
38249         if(this.activePanel){
38250             var el = this.activePanel.getEl();
38251             w = w !== null ? w : el.getWidth();
38252             h = h !== null ? h : el.getHeight();
38253             this.panelSize = {width: w, height: h};
38254             this.activePanel.setSize(w, h);
38255         }
38256         if(Roo.isIE && this.tabs){
38257             this.tabs.el.repaint();
38258         }
38259     },
38260
38261     /**
38262      * Returns the container element for this region.
38263      * @return {Roo.Element}
38264      */
38265     getEl : function(){
38266         return this.el;
38267     },
38268
38269     /**
38270      * Hides this region.
38271      */
38272     hide : function(){
38273         //if(!this.collapsed){
38274             this.el.dom.style.left = "-2000px";
38275             this.el.hide();
38276         //}else{
38277          //   this.collapsedEl.dom.style.left = "-2000px";
38278          //   this.collapsedEl.hide();
38279        // }
38280         this.visible = false;
38281         this.fireEvent("visibilitychange", this, false);
38282     },
38283
38284     /**
38285      * Shows this region if it was previously hidden.
38286      */
38287     show : function(){
38288         //if(!this.collapsed){
38289             this.el.show();
38290         //}else{
38291         //    this.collapsedEl.show();
38292        // }
38293         this.visible = true;
38294         this.fireEvent("visibilitychange", this, true);
38295     },
38296 /*
38297     closeClicked : function(){
38298         if(this.activePanel){
38299             this.remove(this.activePanel);
38300         }
38301     },
38302
38303     collapseClick : function(e){
38304         if(this.isSlid){
38305            e.stopPropagation();
38306            this.slideIn();
38307         }else{
38308            e.stopPropagation();
38309            this.slideOut();
38310         }
38311     },
38312 */
38313     /**
38314      * Collapses this region.
38315      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38316      */
38317     /*
38318     collapse : function(skipAnim, skipCheck = false){
38319         if(this.collapsed) {
38320             return;
38321         }
38322         
38323         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38324             
38325             this.collapsed = true;
38326             if(this.split){
38327                 this.split.el.hide();
38328             }
38329             if(this.config.animate && skipAnim !== true){
38330                 this.fireEvent("invalidated", this);
38331                 this.animateCollapse();
38332             }else{
38333                 this.el.setLocation(-20000,-20000);
38334                 this.el.hide();
38335                 this.collapsedEl.show();
38336                 this.fireEvent("collapsed", this);
38337                 this.fireEvent("invalidated", this);
38338             }
38339         }
38340         
38341     },
38342 */
38343     animateCollapse : function(){
38344         // overridden
38345     },
38346
38347     /**
38348      * Expands this region if it was previously collapsed.
38349      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38350      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38351      */
38352     /*
38353     expand : function(e, skipAnim){
38354         if(e) {
38355             e.stopPropagation();
38356         }
38357         if(!this.collapsed || this.el.hasActiveFx()) {
38358             return;
38359         }
38360         if(this.isSlid){
38361             this.afterSlideIn();
38362             skipAnim = true;
38363         }
38364         this.collapsed = false;
38365         if(this.config.animate && skipAnim !== true){
38366             this.animateExpand();
38367         }else{
38368             this.el.show();
38369             if(this.split){
38370                 this.split.el.show();
38371             }
38372             this.collapsedEl.setLocation(-2000,-2000);
38373             this.collapsedEl.hide();
38374             this.fireEvent("invalidated", this);
38375             this.fireEvent("expanded", this);
38376         }
38377     },
38378 */
38379     animateExpand : function(){
38380         // overridden
38381     },
38382
38383     initTabs : function()
38384     {
38385         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38386         
38387         var ts = new Roo.bootstrap.panel.Tabs({
38388             el: this.bodyEl.dom,
38389             region : this,
38390             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38391             disableTooltips: this.config.disableTabTips,
38392             toolbar : this.config.toolbar
38393         });
38394         
38395         if(this.config.hideTabs){
38396             ts.stripWrap.setDisplayed(false);
38397         }
38398         this.tabs = ts;
38399         ts.resizeTabs = this.config.resizeTabs === true;
38400         ts.minTabWidth = this.config.minTabWidth || 40;
38401         ts.maxTabWidth = this.config.maxTabWidth || 250;
38402         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38403         ts.monitorResize = false;
38404         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38405         ts.bodyEl.addClass('roo-layout-tabs-body');
38406         this.panels.each(this.initPanelAsTab, this);
38407     },
38408
38409     initPanelAsTab : function(panel){
38410         var ti = this.tabs.addTab(
38411             panel.getEl().id,
38412             panel.getTitle(),
38413             null,
38414             this.config.closeOnTab && panel.isClosable(),
38415             panel.tpl
38416         );
38417         if(panel.tabTip !== undefined){
38418             ti.setTooltip(panel.tabTip);
38419         }
38420         ti.on("activate", function(){
38421               this.setActivePanel(panel);
38422         }, this);
38423         
38424         if(this.config.closeOnTab){
38425             ti.on("beforeclose", function(t, e){
38426                 e.cancel = true;
38427                 this.remove(panel);
38428             }, this);
38429         }
38430         
38431         panel.tabItem = ti;
38432         
38433         return ti;
38434     },
38435
38436     updatePanelTitle : function(panel, title)
38437     {
38438         if(this.activePanel == panel){
38439             this.updateTitle(title);
38440         }
38441         if(this.tabs){
38442             var ti = this.tabs.getTab(panel.getEl().id);
38443             ti.setText(title);
38444             if(panel.tabTip !== undefined){
38445                 ti.setTooltip(panel.tabTip);
38446             }
38447         }
38448     },
38449
38450     updateTitle : function(title){
38451         if(this.titleTextEl && !this.config.title){
38452             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38453         }
38454     },
38455
38456     setActivePanel : function(panel)
38457     {
38458         panel = this.getPanel(panel);
38459         if(this.activePanel && this.activePanel != panel){
38460             if(this.activePanel.setActiveState(false) === false){
38461                 return;
38462             }
38463         }
38464         this.activePanel = panel;
38465         panel.setActiveState(true);
38466         if(this.panelSize){
38467             panel.setSize(this.panelSize.width, this.panelSize.height);
38468         }
38469         if(this.closeBtn){
38470             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38471         }
38472         this.updateTitle(panel.getTitle());
38473         if(this.tabs){
38474             this.fireEvent("invalidated", this);
38475         }
38476         this.fireEvent("panelactivated", this, panel);
38477     },
38478
38479     /**
38480      * Shows the specified panel.
38481      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38482      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38483      */
38484     showPanel : function(panel)
38485     {
38486         panel = this.getPanel(panel);
38487         if(panel){
38488             if(this.tabs){
38489                 var tab = this.tabs.getTab(panel.getEl().id);
38490                 if(tab.isHidden()){
38491                     this.tabs.unhideTab(tab.id);
38492                 }
38493                 tab.activate();
38494             }else{
38495                 this.setActivePanel(panel);
38496             }
38497         }
38498         return panel;
38499     },
38500
38501     /**
38502      * Get the active panel for this region.
38503      * @return {Roo.ContentPanel} The active panel or null
38504      */
38505     getActivePanel : function(){
38506         return this.activePanel;
38507     },
38508
38509     validateVisibility : function(){
38510         if(this.panels.getCount() < 1){
38511             this.updateTitle("&#160;");
38512             this.closeBtn.hide();
38513             this.hide();
38514         }else{
38515             if(!this.isVisible()){
38516                 this.show();
38517             }
38518         }
38519     },
38520
38521     /**
38522      * Adds the passed ContentPanel(s) to this region.
38523      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38524      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38525      */
38526     add : function(panel)
38527     {
38528         if(arguments.length > 1){
38529             for(var i = 0, len = arguments.length; i < len; i++) {
38530                 this.add(arguments[i]);
38531             }
38532             return null;
38533         }
38534         
38535         // if we have not been rendered yet, then we can not really do much of this..
38536         if (!this.bodyEl) {
38537             this.unrendered_panels.push(panel);
38538             return panel;
38539         }
38540         
38541         
38542         
38543         
38544         if(this.hasPanel(panel)){
38545             this.showPanel(panel);
38546             return panel;
38547         }
38548         panel.setRegion(this);
38549         this.panels.add(panel);
38550        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38551             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38552             // and hide them... ???
38553             this.bodyEl.dom.appendChild(panel.getEl().dom);
38554             if(panel.background !== true){
38555                 this.setActivePanel(panel);
38556             }
38557             this.fireEvent("paneladded", this, panel);
38558             return panel;
38559         }
38560         */
38561         if(!this.tabs){
38562             this.initTabs();
38563         }else{
38564             this.initPanelAsTab(panel);
38565         }
38566         
38567         
38568         if(panel.background !== true){
38569             this.tabs.activate(panel.getEl().id);
38570         }
38571         this.fireEvent("paneladded", this, panel);
38572         return panel;
38573     },
38574
38575     /**
38576      * Hides the tab for the specified panel.
38577      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38578      */
38579     hidePanel : function(panel){
38580         if(this.tabs && (panel = this.getPanel(panel))){
38581             this.tabs.hideTab(panel.getEl().id);
38582         }
38583     },
38584
38585     /**
38586      * Unhides the tab for a previously hidden panel.
38587      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38588      */
38589     unhidePanel : function(panel){
38590         if(this.tabs && (panel = this.getPanel(panel))){
38591             this.tabs.unhideTab(panel.getEl().id);
38592         }
38593     },
38594
38595     clearPanels : function(){
38596         while(this.panels.getCount() > 0){
38597              this.remove(this.panels.first());
38598         }
38599     },
38600
38601     /**
38602      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38603      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38604      * @param {Boolean} preservePanel Overrides the config preservePanel option
38605      * @return {Roo.ContentPanel} The panel that was removed
38606      */
38607     remove : function(panel, preservePanel)
38608     {
38609         panel = this.getPanel(panel);
38610         if(!panel){
38611             return null;
38612         }
38613         var e = {};
38614         this.fireEvent("beforeremove", this, panel, e);
38615         if(e.cancel === true){
38616             return null;
38617         }
38618         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38619         var panelId = panel.getId();
38620         this.panels.removeKey(panelId);
38621         if(preservePanel){
38622             document.body.appendChild(panel.getEl().dom);
38623         }
38624         if(this.tabs){
38625             this.tabs.removeTab(panel.getEl().id);
38626         }else if (!preservePanel){
38627             this.bodyEl.dom.removeChild(panel.getEl().dom);
38628         }
38629         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38630             var p = this.panels.first();
38631             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38632             tempEl.appendChild(p.getEl().dom);
38633             this.bodyEl.update("");
38634             this.bodyEl.dom.appendChild(p.getEl().dom);
38635             tempEl = null;
38636             this.updateTitle(p.getTitle());
38637             this.tabs = null;
38638             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38639             this.setActivePanel(p);
38640         }
38641         panel.setRegion(null);
38642         if(this.activePanel == panel){
38643             this.activePanel = null;
38644         }
38645         if(this.config.autoDestroy !== false && preservePanel !== true){
38646             try{panel.destroy();}catch(e){}
38647         }
38648         this.fireEvent("panelremoved", this, panel);
38649         return panel;
38650     },
38651
38652     /**
38653      * Returns the TabPanel component used by this region
38654      * @return {Roo.TabPanel}
38655      */
38656     getTabs : function(){
38657         return this.tabs;
38658     },
38659
38660     createTool : function(parentEl, className){
38661         var btn = Roo.DomHelper.append(parentEl, {
38662             tag: "div",
38663             cls: "x-layout-tools-button",
38664             children: [ {
38665                 tag: "div",
38666                 cls: "roo-layout-tools-button-inner " + className,
38667                 html: "&#160;"
38668             }]
38669         }, true);
38670         btn.addClassOnOver("roo-layout-tools-button-over");
38671         return btn;
38672     }
38673 });/*
38674  * Based on:
38675  * Ext JS Library 1.1.1
38676  * Copyright(c) 2006-2007, Ext JS, LLC.
38677  *
38678  * Originally Released Under LGPL - original licence link has changed is not relivant.
38679  *
38680  * Fork - LGPL
38681  * <script type="text/javascript">
38682  */
38683  
38684
38685
38686 /**
38687  * @class Roo.SplitLayoutRegion
38688  * @extends Roo.LayoutRegion
38689  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38690  */
38691 Roo.bootstrap.layout.Split = function(config){
38692     this.cursor = config.cursor;
38693     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38694 };
38695
38696 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38697 {
38698     splitTip : "Drag to resize.",
38699     collapsibleSplitTip : "Drag to resize. Double click to hide.",
38700     useSplitTips : false,
38701
38702     applyConfig : function(config){
38703         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38704     },
38705     
38706     onRender : function(ctr,pos) {
38707         
38708         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38709         if(!this.config.split){
38710             return;
38711         }
38712         if(!this.split){
38713             
38714             var splitEl = Roo.DomHelper.append(ctr.dom,  {
38715                             tag: "div",
38716                             id: this.el.id + "-split",
38717                             cls: "roo-layout-split roo-layout-split-"+this.position,
38718                             html: "&#160;"
38719             });
38720             /** The SplitBar for this region 
38721             * @type Roo.SplitBar */
38722             // does not exist yet...
38723             Roo.log([this.position, this.orientation]);
38724             
38725             this.split = new Roo.bootstrap.SplitBar({
38726                 dragElement : splitEl,
38727                 resizingElement: this.el,
38728                 orientation : this.orientation
38729             });
38730             
38731             this.split.on("moved", this.onSplitMove, this);
38732             this.split.useShim = this.config.useShim === true;
38733             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38734             if(this.useSplitTips){
38735                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38736             }
38737             //if(config.collapsible){
38738             //    this.split.el.on("dblclick", this.collapse,  this);
38739             //}
38740         }
38741         if(typeof this.config.minSize != "undefined"){
38742             this.split.minSize = this.config.minSize;
38743         }
38744         if(typeof this.config.maxSize != "undefined"){
38745             this.split.maxSize = this.config.maxSize;
38746         }
38747         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38748             this.hideSplitter();
38749         }
38750         
38751     },
38752
38753     getHMaxSize : function(){
38754          var cmax = this.config.maxSize || 10000;
38755          var center = this.mgr.getRegion("center");
38756          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38757     },
38758
38759     getVMaxSize : function(){
38760          var cmax = this.config.maxSize || 10000;
38761          var center = this.mgr.getRegion("center");
38762          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38763     },
38764
38765     onSplitMove : function(split, newSize){
38766         this.fireEvent("resized", this, newSize);
38767     },
38768     
38769     /** 
38770      * Returns the {@link Roo.SplitBar} for this region.
38771      * @return {Roo.SplitBar}
38772      */
38773     getSplitBar : function(){
38774         return this.split;
38775     },
38776     
38777     hide : function(){
38778         this.hideSplitter();
38779         Roo.bootstrap.layout.Split.superclass.hide.call(this);
38780     },
38781
38782     hideSplitter : function(){
38783         if(this.split){
38784             this.split.el.setLocation(-2000,-2000);
38785             this.split.el.hide();
38786         }
38787     },
38788
38789     show : function(){
38790         if(this.split){
38791             this.split.el.show();
38792         }
38793         Roo.bootstrap.layout.Split.superclass.show.call(this);
38794     },
38795     
38796     beforeSlide: function(){
38797         if(Roo.isGecko){// firefox overflow auto bug workaround
38798             this.bodyEl.clip();
38799             if(this.tabs) {
38800                 this.tabs.bodyEl.clip();
38801             }
38802             if(this.activePanel){
38803                 this.activePanel.getEl().clip();
38804                 
38805                 if(this.activePanel.beforeSlide){
38806                     this.activePanel.beforeSlide();
38807                 }
38808             }
38809         }
38810     },
38811     
38812     afterSlide : function(){
38813         if(Roo.isGecko){// firefox overflow auto bug workaround
38814             this.bodyEl.unclip();
38815             if(this.tabs) {
38816                 this.tabs.bodyEl.unclip();
38817             }
38818             if(this.activePanel){
38819                 this.activePanel.getEl().unclip();
38820                 if(this.activePanel.afterSlide){
38821                     this.activePanel.afterSlide();
38822                 }
38823             }
38824         }
38825     },
38826
38827     initAutoHide : function(){
38828         if(this.autoHide !== false){
38829             if(!this.autoHideHd){
38830                 var st = new Roo.util.DelayedTask(this.slideIn, this);
38831                 this.autoHideHd = {
38832                     "mouseout": function(e){
38833                         if(!e.within(this.el, true)){
38834                             st.delay(500);
38835                         }
38836                     },
38837                     "mouseover" : function(e){
38838                         st.cancel();
38839                     },
38840                     scope : this
38841                 };
38842             }
38843             this.el.on(this.autoHideHd);
38844         }
38845     },
38846
38847     clearAutoHide : function(){
38848         if(this.autoHide !== false){
38849             this.el.un("mouseout", this.autoHideHd.mouseout);
38850             this.el.un("mouseover", this.autoHideHd.mouseover);
38851         }
38852     },
38853
38854     clearMonitor : function(){
38855         Roo.get(document).un("click", this.slideInIf, this);
38856     },
38857
38858     // these names are backwards but not changed for compat
38859     slideOut : function(){
38860         if(this.isSlid || this.el.hasActiveFx()){
38861             return;
38862         }
38863         this.isSlid = true;
38864         if(this.collapseBtn){
38865             this.collapseBtn.hide();
38866         }
38867         this.closeBtnState = this.closeBtn.getStyle('display');
38868         this.closeBtn.hide();
38869         if(this.stickBtn){
38870             this.stickBtn.show();
38871         }
38872         this.el.show();
38873         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
38874         this.beforeSlide();
38875         this.el.setStyle("z-index", 10001);
38876         this.el.slideIn(this.getSlideAnchor(), {
38877             callback: function(){
38878                 this.afterSlide();
38879                 this.initAutoHide();
38880                 Roo.get(document).on("click", this.slideInIf, this);
38881                 this.fireEvent("slideshow", this);
38882             },
38883             scope: this,
38884             block: true
38885         });
38886     },
38887
38888     afterSlideIn : function(){
38889         this.clearAutoHide();
38890         this.isSlid = false;
38891         this.clearMonitor();
38892         this.el.setStyle("z-index", "");
38893         if(this.collapseBtn){
38894             this.collapseBtn.show();
38895         }
38896         this.closeBtn.setStyle('display', this.closeBtnState);
38897         if(this.stickBtn){
38898             this.stickBtn.hide();
38899         }
38900         this.fireEvent("slidehide", this);
38901     },
38902
38903     slideIn : function(cb){
38904         if(!this.isSlid || this.el.hasActiveFx()){
38905             Roo.callback(cb);
38906             return;
38907         }
38908         this.isSlid = false;
38909         this.beforeSlide();
38910         this.el.slideOut(this.getSlideAnchor(), {
38911             callback: function(){
38912                 this.el.setLeftTop(-10000, -10000);
38913                 this.afterSlide();
38914                 this.afterSlideIn();
38915                 Roo.callback(cb);
38916             },
38917             scope: this,
38918             block: true
38919         });
38920     },
38921     
38922     slideInIf : function(e){
38923         if(!e.within(this.el)){
38924             this.slideIn();
38925         }
38926     },
38927
38928     animateCollapse : function(){
38929         this.beforeSlide();
38930         this.el.setStyle("z-index", 20000);
38931         var anchor = this.getSlideAnchor();
38932         this.el.slideOut(anchor, {
38933             callback : function(){
38934                 this.el.setStyle("z-index", "");
38935                 this.collapsedEl.slideIn(anchor, {duration:.3});
38936                 this.afterSlide();
38937                 this.el.setLocation(-10000,-10000);
38938                 this.el.hide();
38939                 this.fireEvent("collapsed", this);
38940             },
38941             scope: this,
38942             block: true
38943         });
38944     },
38945
38946     animateExpand : function(){
38947         this.beforeSlide();
38948         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
38949         this.el.setStyle("z-index", 20000);
38950         this.collapsedEl.hide({
38951             duration:.1
38952         });
38953         this.el.slideIn(this.getSlideAnchor(), {
38954             callback : function(){
38955                 this.el.setStyle("z-index", "");
38956                 this.afterSlide();
38957                 if(this.split){
38958                     this.split.el.show();
38959                 }
38960                 this.fireEvent("invalidated", this);
38961                 this.fireEvent("expanded", this);
38962             },
38963             scope: this,
38964             block: true
38965         });
38966     },
38967
38968     anchors : {
38969         "west" : "left",
38970         "east" : "right",
38971         "north" : "top",
38972         "south" : "bottom"
38973     },
38974
38975     sanchors : {
38976         "west" : "l",
38977         "east" : "r",
38978         "north" : "t",
38979         "south" : "b"
38980     },
38981
38982     canchors : {
38983         "west" : "tl-tr",
38984         "east" : "tr-tl",
38985         "north" : "tl-bl",
38986         "south" : "bl-tl"
38987     },
38988
38989     getAnchor : function(){
38990         return this.anchors[this.position];
38991     },
38992
38993     getCollapseAnchor : function(){
38994         return this.canchors[this.position];
38995     },
38996
38997     getSlideAnchor : function(){
38998         return this.sanchors[this.position];
38999     },
39000
39001     getAlignAdj : function(){
39002         var cm = this.cmargins;
39003         switch(this.position){
39004             case "west":
39005                 return [0, 0];
39006             break;
39007             case "east":
39008                 return [0, 0];
39009             break;
39010             case "north":
39011                 return [0, 0];
39012             break;
39013             case "south":
39014                 return [0, 0];
39015             break;
39016         }
39017     },
39018
39019     getExpandAdj : function(){
39020         var c = this.collapsedEl, cm = this.cmargins;
39021         switch(this.position){
39022             case "west":
39023                 return [-(cm.right+c.getWidth()+cm.left), 0];
39024             break;
39025             case "east":
39026                 return [cm.right+c.getWidth()+cm.left, 0];
39027             break;
39028             case "north":
39029                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39030             break;
39031             case "south":
39032                 return [0, cm.top+cm.bottom+c.getHeight()];
39033             break;
39034         }
39035     }
39036 });/*
39037  * Based on:
39038  * Ext JS Library 1.1.1
39039  * Copyright(c) 2006-2007, Ext JS, LLC.
39040  *
39041  * Originally Released Under LGPL - original licence link has changed is not relivant.
39042  *
39043  * Fork - LGPL
39044  * <script type="text/javascript">
39045  */
39046 /*
39047  * These classes are private internal classes
39048  */
39049 Roo.bootstrap.layout.Center = function(config){
39050     config.region = "center";
39051     Roo.bootstrap.layout.Region.call(this, config);
39052     this.visible = true;
39053     this.minWidth = config.minWidth || 20;
39054     this.minHeight = config.minHeight || 20;
39055 };
39056
39057 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39058     hide : function(){
39059         // center panel can't be hidden
39060     },
39061     
39062     show : function(){
39063         // center panel can't be hidden
39064     },
39065     
39066     getMinWidth: function(){
39067         return this.minWidth;
39068     },
39069     
39070     getMinHeight: function(){
39071         return this.minHeight;
39072     }
39073 });
39074
39075
39076
39077
39078  
39079
39080
39081
39082
39083
39084
39085 Roo.bootstrap.layout.North = function(config)
39086 {
39087     config.region = 'north';
39088     config.cursor = 'n-resize';
39089     
39090     Roo.bootstrap.layout.Split.call(this, config);
39091     
39092     
39093     if(this.split){
39094         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39095         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39096         this.split.el.addClass("roo-layout-split-v");
39097     }
39098     var size = config.initialSize || config.height;
39099     if(typeof size != "undefined"){
39100         this.el.setHeight(size);
39101     }
39102 };
39103 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39104 {
39105     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39106     
39107     
39108     
39109     getBox : function(){
39110         if(this.collapsed){
39111             return this.collapsedEl.getBox();
39112         }
39113         var box = this.el.getBox();
39114         if(this.split){
39115             box.height += this.split.el.getHeight();
39116         }
39117         return box;
39118     },
39119     
39120     updateBox : function(box){
39121         if(this.split && !this.collapsed){
39122             box.height -= this.split.el.getHeight();
39123             this.split.el.setLeft(box.x);
39124             this.split.el.setTop(box.y+box.height);
39125             this.split.el.setWidth(box.width);
39126         }
39127         if(this.collapsed){
39128             this.updateBody(box.width, null);
39129         }
39130         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39131     }
39132 });
39133
39134
39135
39136
39137
39138 Roo.bootstrap.layout.South = function(config){
39139     config.region = 'south';
39140     config.cursor = 's-resize';
39141     Roo.bootstrap.layout.Split.call(this, config);
39142     if(this.split){
39143         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39144         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39145         this.split.el.addClass("roo-layout-split-v");
39146     }
39147     var size = config.initialSize || config.height;
39148     if(typeof size != "undefined"){
39149         this.el.setHeight(size);
39150     }
39151 };
39152
39153 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39154     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39155     getBox : function(){
39156         if(this.collapsed){
39157             return this.collapsedEl.getBox();
39158         }
39159         var box = this.el.getBox();
39160         if(this.split){
39161             var sh = this.split.el.getHeight();
39162             box.height += sh;
39163             box.y -= sh;
39164         }
39165         return box;
39166     },
39167     
39168     updateBox : function(box){
39169         if(this.split && !this.collapsed){
39170             var sh = this.split.el.getHeight();
39171             box.height -= sh;
39172             box.y += sh;
39173             this.split.el.setLeft(box.x);
39174             this.split.el.setTop(box.y-sh);
39175             this.split.el.setWidth(box.width);
39176         }
39177         if(this.collapsed){
39178             this.updateBody(box.width, null);
39179         }
39180         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39181     }
39182 });
39183
39184 Roo.bootstrap.layout.East = function(config){
39185     config.region = "east";
39186     config.cursor = "e-resize";
39187     Roo.bootstrap.layout.Split.call(this, config);
39188     if(this.split){
39189         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39190         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39191         this.split.el.addClass("roo-layout-split-h");
39192     }
39193     var size = config.initialSize || config.width;
39194     if(typeof size != "undefined"){
39195         this.el.setWidth(size);
39196     }
39197 };
39198 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39199     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39200     getBox : function(){
39201         if(this.collapsed){
39202             return this.collapsedEl.getBox();
39203         }
39204         var box = this.el.getBox();
39205         if(this.split){
39206             var sw = this.split.el.getWidth();
39207             box.width += sw;
39208             box.x -= sw;
39209         }
39210         return box;
39211     },
39212
39213     updateBox : function(box){
39214         if(this.split && !this.collapsed){
39215             var sw = this.split.el.getWidth();
39216             box.width -= sw;
39217             this.split.el.setLeft(box.x);
39218             this.split.el.setTop(box.y);
39219             this.split.el.setHeight(box.height);
39220             box.x += sw;
39221         }
39222         if(this.collapsed){
39223             this.updateBody(null, box.height);
39224         }
39225         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39226     }
39227 });
39228
39229 Roo.bootstrap.layout.West = function(config){
39230     config.region = "west";
39231     config.cursor = "w-resize";
39232     
39233     Roo.bootstrap.layout.Split.call(this, config);
39234     if(this.split){
39235         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39236         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39237         this.split.el.addClass("roo-layout-split-h");
39238     }
39239     
39240 };
39241 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39242     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39243     
39244     onRender: function(ctr, pos)
39245     {
39246         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39247         var size = this.config.initialSize || this.config.width;
39248         if(typeof size != "undefined"){
39249             this.el.setWidth(size);
39250         }
39251     },
39252     
39253     getBox : function(){
39254         if(this.collapsed){
39255             return this.collapsedEl.getBox();
39256         }
39257         var box = this.el.getBox();
39258         if(this.split){
39259             box.width += this.split.el.getWidth();
39260         }
39261         return box;
39262     },
39263     
39264     updateBox : function(box){
39265         if(this.split && !this.collapsed){
39266             var sw = this.split.el.getWidth();
39267             box.width -= sw;
39268             this.split.el.setLeft(box.x+box.width);
39269             this.split.el.setTop(box.y);
39270             this.split.el.setHeight(box.height);
39271         }
39272         if(this.collapsed){
39273             this.updateBody(null, box.height);
39274         }
39275         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39276     }
39277 });Roo.namespace("Roo.bootstrap.panel");/*
39278  * Based on:
39279  * Ext JS Library 1.1.1
39280  * Copyright(c) 2006-2007, Ext JS, LLC.
39281  *
39282  * Originally Released Under LGPL - original licence link has changed is not relivant.
39283  *
39284  * Fork - LGPL
39285  * <script type="text/javascript">
39286  */
39287 /**
39288  * @class Roo.ContentPanel
39289  * @extends Roo.util.Observable
39290  * A basic ContentPanel element.
39291  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39292  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39293  * @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
39294  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39295  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39296  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39297  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39298  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39299  * @cfg {String} title          The title for this panel
39300  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39301  * @cfg {String} url            Calls {@link #setUrl} with this value
39302  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39303  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39304  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39305  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39306  * @cfg {Boolean} badges render the badges
39307  * @cfg {String} cls  extra classes to use  
39308  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39309
39310  * @constructor
39311  * Create a new ContentPanel.
39312  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39313  * @param {String/Object} config A string to set only the title or a config object
39314  * @param {String} content (optional) Set the HTML content for this panel
39315  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39316  */
39317 Roo.bootstrap.panel.Content = function( config){
39318     
39319     this.tpl = config.tpl || false;
39320     
39321     var el = config.el;
39322     var content = config.content;
39323
39324     if(config.autoCreate){ // xtype is available if this is called from factory
39325         el = Roo.id();
39326     }
39327     this.el = Roo.get(el);
39328     if(!this.el && config && config.autoCreate){
39329         if(typeof config.autoCreate == "object"){
39330             if(!config.autoCreate.id){
39331                 config.autoCreate.id = config.id||el;
39332             }
39333             this.el = Roo.DomHelper.append(document.body,
39334                         config.autoCreate, true);
39335         }else{
39336             var elcfg =  {
39337                 tag: "div",
39338                 cls: (config.cls || '') +
39339                     (config.background ? ' bg-' + config.background : '') +
39340                     " roo-layout-inactive-content",
39341                 id: config.id||el
39342             };
39343             if (config.html) {
39344                 elcfg.html = config.html;
39345                 
39346             }
39347                         
39348             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39349         }
39350     } 
39351     this.closable = false;
39352     this.loaded = false;
39353     this.active = false;
39354    
39355       
39356     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39357         
39358         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39359         
39360         this.wrapEl = this.el; //this.el.wrap();
39361         var ti = [];
39362         if (config.toolbar.items) {
39363             ti = config.toolbar.items ;
39364             delete config.toolbar.items ;
39365         }
39366         
39367         var nitems = [];
39368         this.toolbar.render(this.wrapEl, 'before');
39369         for(var i =0;i < ti.length;i++) {
39370           //  Roo.log(['add child', items[i]]);
39371             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39372         }
39373         this.toolbar.items = nitems;
39374         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39375         delete config.toolbar;
39376         
39377     }
39378     /*
39379     // xtype created footer. - not sure if will work as we normally have to render first..
39380     if (this.footer && !this.footer.el && this.footer.xtype) {
39381         if (!this.wrapEl) {
39382             this.wrapEl = this.el.wrap();
39383         }
39384     
39385         this.footer.container = this.wrapEl.createChild();
39386          
39387         this.footer = Roo.factory(this.footer, Roo);
39388         
39389     }
39390     */
39391     
39392      if(typeof config == "string"){
39393         this.title = config;
39394     }else{
39395         Roo.apply(this, config);
39396     }
39397     
39398     if(this.resizeEl){
39399         this.resizeEl = Roo.get(this.resizeEl, true);
39400     }else{
39401         this.resizeEl = this.el;
39402     }
39403     // handle view.xtype
39404     
39405  
39406     
39407     
39408     this.addEvents({
39409         /**
39410          * @event activate
39411          * Fires when this panel is activated. 
39412          * @param {Roo.ContentPanel} this
39413          */
39414         "activate" : true,
39415         /**
39416          * @event deactivate
39417          * Fires when this panel is activated. 
39418          * @param {Roo.ContentPanel} this
39419          */
39420         "deactivate" : true,
39421
39422         /**
39423          * @event resize
39424          * Fires when this panel is resized if fitToFrame is true.
39425          * @param {Roo.ContentPanel} this
39426          * @param {Number} width The width after any component adjustments
39427          * @param {Number} height The height after any component adjustments
39428          */
39429         "resize" : true,
39430         
39431          /**
39432          * @event render
39433          * Fires when this tab is created
39434          * @param {Roo.ContentPanel} this
39435          */
39436         "render" : true
39437         
39438         
39439         
39440     });
39441     
39442
39443     
39444     
39445     if(this.autoScroll){
39446         this.resizeEl.setStyle("overflow", "auto");
39447     } else {
39448         // fix randome scrolling
39449         //this.el.on('scroll', function() {
39450         //    Roo.log('fix random scolling');
39451         //    this.scrollTo('top',0); 
39452         //});
39453     }
39454     content = content || this.content;
39455     if(content){
39456         this.setContent(content);
39457     }
39458     if(config && config.url){
39459         this.setUrl(this.url, this.params, this.loadOnce);
39460     }
39461     
39462     
39463     
39464     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39465     
39466     if (this.view && typeof(this.view.xtype) != 'undefined') {
39467         this.view.el = this.el.appendChild(document.createElement("div"));
39468         this.view = Roo.factory(this.view); 
39469         this.view.render  &&  this.view.render(false, '');  
39470     }
39471     
39472     
39473     this.fireEvent('render', this);
39474 };
39475
39476 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39477     
39478     cls : '',
39479     background : '',
39480     
39481     tabTip : '',
39482     
39483     setRegion : function(region){
39484         this.region = region;
39485         this.setActiveClass(region && !this.background);
39486     },
39487     
39488     
39489     setActiveClass: function(state)
39490     {
39491         if(state){
39492            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39493            this.el.setStyle('position','relative');
39494         }else{
39495            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39496            this.el.setStyle('position', 'absolute');
39497         } 
39498     },
39499     
39500     /**
39501      * Returns the toolbar for this Panel if one was configured. 
39502      * @return {Roo.Toolbar} 
39503      */
39504     getToolbar : function(){
39505         return this.toolbar;
39506     },
39507     
39508     setActiveState : function(active)
39509     {
39510         this.active = active;
39511         this.setActiveClass(active);
39512         if(!active){
39513             if(this.fireEvent("deactivate", this) === false){
39514                 return false;
39515             }
39516             return true;
39517         }
39518         this.fireEvent("activate", this);
39519         return true;
39520     },
39521     /**
39522      * Updates this panel's element
39523      * @param {String} content The new content
39524      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39525     */
39526     setContent : function(content, loadScripts){
39527         this.el.update(content, loadScripts);
39528     },
39529
39530     ignoreResize : function(w, h){
39531         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39532             return true;
39533         }else{
39534             this.lastSize = {width: w, height: h};
39535             return false;
39536         }
39537     },
39538     /**
39539      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39540      * @return {Roo.UpdateManager} The UpdateManager
39541      */
39542     getUpdateManager : function(){
39543         return this.el.getUpdateManager();
39544     },
39545      /**
39546      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39547      * @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:
39548 <pre><code>
39549 panel.load({
39550     url: "your-url.php",
39551     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39552     callback: yourFunction,
39553     scope: yourObject, //(optional scope)
39554     discardUrl: false,
39555     nocache: false,
39556     text: "Loading...",
39557     timeout: 30,
39558     scripts: false
39559 });
39560 </code></pre>
39561      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39562      * 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.
39563      * @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}
39564      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39565      * @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.
39566      * @return {Roo.ContentPanel} this
39567      */
39568     load : function(){
39569         var um = this.el.getUpdateManager();
39570         um.update.apply(um, arguments);
39571         return this;
39572     },
39573
39574
39575     /**
39576      * 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.
39577      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39578      * @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)
39579      * @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)
39580      * @return {Roo.UpdateManager} The UpdateManager
39581      */
39582     setUrl : function(url, params, loadOnce){
39583         if(this.refreshDelegate){
39584             this.removeListener("activate", this.refreshDelegate);
39585         }
39586         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39587         this.on("activate", this.refreshDelegate);
39588         return this.el.getUpdateManager();
39589     },
39590     
39591     _handleRefresh : function(url, params, loadOnce){
39592         if(!loadOnce || !this.loaded){
39593             var updater = this.el.getUpdateManager();
39594             updater.update(url, params, this._setLoaded.createDelegate(this));
39595         }
39596     },
39597     
39598     _setLoaded : function(){
39599         this.loaded = true;
39600     }, 
39601     
39602     /**
39603      * Returns this panel's id
39604      * @return {String} 
39605      */
39606     getId : function(){
39607         return this.el.id;
39608     },
39609     
39610     /** 
39611      * Returns this panel's element - used by regiosn to add.
39612      * @return {Roo.Element} 
39613      */
39614     getEl : function(){
39615         return this.wrapEl || this.el;
39616     },
39617     
39618    
39619     
39620     adjustForComponents : function(width, height)
39621     {
39622         //Roo.log('adjustForComponents ');
39623         if(this.resizeEl != this.el){
39624             width -= this.el.getFrameWidth('lr');
39625             height -= this.el.getFrameWidth('tb');
39626         }
39627         if(this.toolbar){
39628             var te = this.toolbar.getEl();
39629             te.setWidth(width);
39630             height -= te.getHeight();
39631         }
39632         if(this.footer){
39633             var te = this.footer.getEl();
39634             te.setWidth(width);
39635             height -= te.getHeight();
39636         }
39637         
39638         
39639         if(this.adjustments){
39640             width += this.adjustments[0];
39641             height += this.adjustments[1];
39642         }
39643         return {"width": width, "height": height};
39644     },
39645     
39646     setSize : function(width, height){
39647         if(this.fitToFrame && !this.ignoreResize(width, height)){
39648             if(this.fitContainer && this.resizeEl != this.el){
39649                 this.el.setSize(width, height);
39650             }
39651             var size = this.adjustForComponents(width, height);
39652             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39653             this.fireEvent('resize', this, size.width, size.height);
39654         }
39655     },
39656     
39657     /**
39658      * Returns this panel's title
39659      * @return {String} 
39660      */
39661     getTitle : function(){
39662         
39663         if (typeof(this.title) != 'object') {
39664             return this.title;
39665         }
39666         
39667         var t = '';
39668         for (var k in this.title) {
39669             if (!this.title.hasOwnProperty(k)) {
39670                 continue;
39671             }
39672             
39673             if (k.indexOf('-') >= 0) {
39674                 var s = k.split('-');
39675                 for (var i = 0; i<s.length; i++) {
39676                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39677                 }
39678             } else {
39679                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39680             }
39681         }
39682         return t;
39683     },
39684     
39685     /**
39686      * Set this panel's title
39687      * @param {String} title
39688      */
39689     setTitle : function(title){
39690         this.title = title;
39691         if(this.region){
39692             this.region.updatePanelTitle(this, title);
39693         }
39694     },
39695     
39696     /**
39697      * Returns true is this panel was configured to be closable
39698      * @return {Boolean} 
39699      */
39700     isClosable : function(){
39701         return this.closable;
39702     },
39703     
39704     beforeSlide : function(){
39705         this.el.clip();
39706         this.resizeEl.clip();
39707     },
39708     
39709     afterSlide : function(){
39710         this.el.unclip();
39711         this.resizeEl.unclip();
39712     },
39713     
39714     /**
39715      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
39716      *   Will fail silently if the {@link #setUrl} method has not been called.
39717      *   This does not activate the panel, just updates its content.
39718      */
39719     refresh : function(){
39720         if(this.refreshDelegate){
39721            this.loaded = false;
39722            this.refreshDelegate();
39723         }
39724     },
39725     
39726     /**
39727      * Destroys this panel
39728      */
39729     destroy : function(){
39730         this.el.removeAllListeners();
39731         var tempEl = document.createElement("span");
39732         tempEl.appendChild(this.el.dom);
39733         tempEl.innerHTML = "";
39734         this.el.remove();
39735         this.el = null;
39736     },
39737     
39738     /**
39739      * form - if the content panel contains a form - this is a reference to it.
39740      * @type {Roo.form.Form}
39741      */
39742     form : false,
39743     /**
39744      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
39745      *    This contains a reference to it.
39746      * @type {Roo.View}
39747      */
39748     view : false,
39749     
39750       /**
39751      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
39752      * <pre><code>
39753
39754 layout.addxtype({
39755        xtype : 'Form',
39756        items: [ .... ]
39757    }
39758 );
39759
39760 </code></pre>
39761      * @param {Object} cfg Xtype definition of item to add.
39762      */
39763     
39764     
39765     getChildContainer: function () {
39766         return this.getEl();
39767     }
39768     
39769     
39770     /*
39771         var  ret = new Roo.factory(cfg);
39772         return ret;
39773         
39774         
39775         // add form..
39776         if (cfg.xtype.match(/^Form$/)) {
39777             
39778             var el;
39779             //if (this.footer) {
39780             //    el = this.footer.container.insertSibling(false, 'before');
39781             //} else {
39782                 el = this.el.createChild();
39783             //}
39784
39785             this.form = new  Roo.form.Form(cfg);
39786             
39787             
39788             if ( this.form.allItems.length) {
39789                 this.form.render(el.dom);
39790             }
39791             return this.form;
39792         }
39793         // should only have one of theses..
39794         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
39795             // views.. should not be just added - used named prop 'view''
39796             
39797             cfg.el = this.el.appendChild(document.createElement("div"));
39798             // factory?
39799             
39800             var ret = new Roo.factory(cfg);
39801              
39802              ret.render && ret.render(false, ''); // render blank..
39803             this.view = ret;
39804             return ret;
39805         }
39806         return false;
39807     }
39808     \*/
39809 });
39810  
39811 /**
39812  * @class Roo.bootstrap.panel.Grid
39813  * @extends Roo.bootstrap.panel.Content
39814  * @constructor
39815  * Create a new GridPanel.
39816  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
39817  * @param {Object} config A the config object
39818   
39819  */
39820
39821
39822
39823 Roo.bootstrap.panel.Grid = function(config)
39824 {
39825     
39826       
39827     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
39828         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
39829
39830     config.el = this.wrapper;
39831     //this.el = this.wrapper;
39832     
39833       if (config.container) {
39834         // ctor'ed from a Border/panel.grid
39835         
39836         
39837         this.wrapper.setStyle("overflow", "hidden");
39838         this.wrapper.addClass('roo-grid-container');
39839
39840     }
39841     
39842     
39843     if(config.toolbar){
39844         var tool_el = this.wrapper.createChild();    
39845         this.toolbar = Roo.factory(config.toolbar);
39846         var ti = [];
39847         if (config.toolbar.items) {
39848             ti = config.toolbar.items ;
39849             delete config.toolbar.items ;
39850         }
39851         
39852         var nitems = [];
39853         this.toolbar.render(tool_el);
39854         for(var i =0;i < ti.length;i++) {
39855           //  Roo.log(['add child', items[i]]);
39856             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39857         }
39858         this.toolbar.items = nitems;
39859         
39860         delete config.toolbar;
39861     }
39862     
39863     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
39864     config.grid.scrollBody = true;;
39865     config.grid.monitorWindowResize = false; // turn off autosizing
39866     config.grid.autoHeight = false;
39867     config.grid.autoWidth = false;
39868     
39869     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
39870     
39871     if (config.background) {
39872         // render grid on panel activation (if panel background)
39873         this.on('activate', function(gp) {
39874             if (!gp.grid.rendered) {
39875                 gp.grid.render(this.wrapper);
39876                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
39877             }
39878         });
39879             
39880     } else {
39881         this.grid.render(this.wrapper);
39882         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
39883
39884     }
39885     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
39886     // ??? needed ??? config.el = this.wrapper;
39887     
39888     
39889     
39890   
39891     // xtype created footer. - not sure if will work as we normally have to render first..
39892     if (this.footer && !this.footer.el && this.footer.xtype) {
39893         
39894         var ctr = this.grid.getView().getFooterPanel(true);
39895         this.footer.dataSource = this.grid.dataSource;
39896         this.footer = Roo.factory(this.footer, Roo);
39897         this.footer.render(ctr);
39898         
39899     }
39900     
39901     
39902     
39903     
39904      
39905 };
39906
39907 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
39908     getId : function(){
39909         return this.grid.id;
39910     },
39911     
39912     /**
39913      * Returns the grid for this panel
39914      * @return {Roo.bootstrap.Table} 
39915      */
39916     getGrid : function(){
39917         return this.grid;    
39918     },
39919     
39920     setSize : function(width, height){
39921         if(!this.ignoreResize(width, height)){
39922             var grid = this.grid;
39923             var size = this.adjustForComponents(width, height);
39924             // tfoot is not a footer?
39925           
39926             
39927             var gridel = grid.getGridEl();
39928             gridel.setSize(size.width, size.height);
39929             
39930             var tbd = grid.getGridEl().select('tbody', true).first();
39931             var thd = grid.getGridEl().select('thead',true).first();
39932             var tbf= grid.getGridEl().select('tfoot', true).first();
39933
39934             if (tbf) {
39935                 size.height -= thd.getHeight();
39936             }
39937             if (thd) {
39938                 size.height -= thd.getHeight();
39939             }
39940             
39941             tbd.setSize(size.width, size.height );
39942             // this is for the account management tab -seems to work there.
39943             var thd = grid.getGridEl().select('thead',true).first();
39944             //if (tbd) {
39945             //    tbd.setSize(size.width, size.height - thd.getHeight());
39946             //}
39947              
39948             grid.autoSize();
39949         }
39950     },
39951      
39952     
39953     
39954     beforeSlide : function(){
39955         this.grid.getView().scroller.clip();
39956     },
39957     
39958     afterSlide : function(){
39959         this.grid.getView().scroller.unclip();
39960     },
39961     
39962     destroy : function(){
39963         this.grid.destroy();
39964         delete this.grid;
39965         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
39966     }
39967 });
39968
39969 /**
39970  * @class Roo.bootstrap.panel.Nest
39971  * @extends Roo.bootstrap.panel.Content
39972  * @constructor
39973  * Create a new Panel, that can contain a layout.Border.
39974  * 
39975  * 
39976  * @param {Roo.BorderLayout} layout The layout for this panel
39977  * @param {String/Object} config A string to set only the title or a config object
39978  */
39979 Roo.bootstrap.panel.Nest = function(config)
39980 {
39981     // construct with only one argument..
39982     /* FIXME - implement nicer consturctors
39983     if (layout.layout) {
39984         config = layout;
39985         layout = config.layout;
39986         delete config.layout;
39987     }
39988     if (layout.xtype && !layout.getEl) {
39989         // then layout needs constructing..
39990         layout = Roo.factory(layout, Roo);
39991     }
39992     */
39993     
39994     config.el =  config.layout.getEl();
39995     
39996     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
39997     
39998     config.layout.monitorWindowResize = false; // turn off autosizing
39999     this.layout = config.layout;
40000     this.layout.getEl().addClass("roo-layout-nested-layout");
40001     this.layout.parent = this;
40002     
40003     
40004     
40005     
40006 };
40007
40008 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40009
40010     setSize : function(width, height){
40011         if(!this.ignoreResize(width, height)){
40012             var size = this.adjustForComponents(width, height);
40013             var el = this.layout.getEl();
40014             if (size.height < 1) {
40015                 el.setWidth(size.width);   
40016             } else {
40017                 el.setSize(size.width, size.height);
40018             }
40019             var touch = el.dom.offsetWidth;
40020             this.layout.layout();
40021             // ie requires a double layout on the first pass
40022             if(Roo.isIE && !this.initialized){
40023                 this.initialized = true;
40024                 this.layout.layout();
40025             }
40026         }
40027     },
40028     
40029     // activate all subpanels if not currently active..
40030     
40031     setActiveState : function(active){
40032         this.active = active;
40033         this.setActiveClass(active);
40034         
40035         if(!active){
40036             this.fireEvent("deactivate", this);
40037             return;
40038         }
40039         
40040         this.fireEvent("activate", this);
40041         // not sure if this should happen before or after..
40042         if (!this.layout) {
40043             return; // should not happen..
40044         }
40045         var reg = false;
40046         for (var r in this.layout.regions) {
40047             reg = this.layout.getRegion(r);
40048             if (reg.getActivePanel()) {
40049                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40050                 reg.setActivePanel(reg.getActivePanel());
40051                 continue;
40052             }
40053             if (!reg.panels.length) {
40054                 continue;
40055             }
40056             reg.showPanel(reg.getPanel(0));
40057         }
40058         
40059         
40060         
40061         
40062     },
40063     
40064     /**
40065      * Returns the nested BorderLayout for this panel
40066      * @return {Roo.BorderLayout} 
40067      */
40068     getLayout : function(){
40069         return this.layout;
40070     },
40071     
40072      /**
40073      * Adds a xtype elements to the layout of the nested panel
40074      * <pre><code>
40075
40076 panel.addxtype({
40077        xtype : 'ContentPanel',
40078        region: 'west',
40079        items: [ .... ]
40080    }
40081 );
40082
40083 panel.addxtype({
40084         xtype : 'NestedLayoutPanel',
40085         region: 'west',
40086         layout: {
40087            center: { },
40088            west: { }   
40089         },
40090         items : [ ... list of content panels or nested layout panels.. ]
40091    }
40092 );
40093 </code></pre>
40094      * @param {Object} cfg Xtype definition of item to add.
40095      */
40096     addxtype : function(cfg) {
40097         return this.layout.addxtype(cfg);
40098     
40099     }
40100 });/*
40101  * Based on:
40102  * Ext JS Library 1.1.1
40103  * Copyright(c) 2006-2007, Ext JS, LLC.
40104  *
40105  * Originally Released Under LGPL - original licence link has changed is not relivant.
40106  *
40107  * Fork - LGPL
40108  * <script type="text/javascript">
40109  */
40110 /**
40111  * @class Roo.TabPanel
40112  * @extends Roo.util.Observable
40113  * A lightweight tab container.
40114  * <br><br>
40115  * Usage:
40116  * <pre><code>
40117 // basic tabs 1, built from existing content
40118 var tabs = new Roo.TabPanel("tabs1");
40119 tabs.addTab("script", "View Script");
40120 tabs.addTab("markup", "View Markup");
40121 tabs.activate("script");
40122
40123 // more advanced tabs, built from javascript
40124 var jtabs = new Roo.TabPanel("jtabs");
40125 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40126
40127 // set up the UpdateManager
40128 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40129 var updater = tab2.getUpdateManager();
40130 updater.setDefaultUrl("ajax1.htm");
40131 tab2.on('activate', updater.refresh, updater, true);
40132
40133 // Use setUrl for Ajax loading
40134 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40135 tab3.setUrl("ajax2.htm", null, true);
40136
40137 // Disabled tab
40138 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40139 tab4.disable();
40140
40141 jtabs.activate("jtabs-1");
40142  * </code></pre>
40143  * @constructor
40144  * Create a new TabPanel.
40145  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40146  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40147  */
40148 Roo.bootstrap.panel.Tabs = function(config){
40149     /**
40150     * The container element for this TabPanel.
40151     * @type Roo.Element
40152     */
40153     this.el = Roo.get(config.el);
40154     delete config.el;
40155     if(config){
40156         if(typeof config == "boolean"){
40157             this.tabPosition = config ? "bottom" : "top";
40158         }else{
40159             Roo.apply(this, config);
40160         }
40161     }
40162     
40163     if(this.tabPosition == "bottom"){
40164         // if tabs are at the bottom = create the body first.
40165         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40166         this.el.addClass("roo-tabs-bottom");
40167     }
40168     // next create the tabs holders
40169     
40170     if (this.tabPosition == "west"){
40171         
40172         var reg = this.region; // fake it..
40173         while (reg) {
40174             if (!reg.mgr.parent) {
40175                 break;
40176             }
40177             reg = reg.mgr.parent.region;
40178         }
40179         Roo.log("got nest?");
40180         Roo.log(reg);
40181         if (reg.mgr.getRegion('west')) {
40182             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40183             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40184             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40185             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40186             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40187         
40188             
40189         }
40190         
40191         
40192     } else {
40193      
40194         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40195         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40196         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40197         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40198     }
40199     
40200     
40201     if(Roo.isIE){
40202         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40203     }
40204     
40205     // finally - if tabs are at the top, then create the body last..
40206     if(this.tabPosition != "bottom"){
40207         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40208          * @type Roo.Element
40209          */
40210         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40211         this.el.addClass("roo-tabs-top");
40212     }
40213     this.items = [];
40214
40215     this.bodyEl.setStyle("position", "relative");
40216
40217     this.active = null;
40218     this.activateDelegate = this.activate.createDelegate(this);
40219
40220     this.addEvents({
40221         /**
40222          * @event tabchange
40223          * Fires when the active tab changes
40224          * @param {Roo.TabPanel} this
40225          * @param {Roo.TabPanelItem} activePanel The new active tab
40226          */
40227         "tabchange": true,
40228         /**
40229          * @event beforetabchange
40230          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40231          * @param {Roo.TabPanel} this
40232          * @param {Object} e Set cancel to true on this object to cancel the tab change
40233          * @param {Roo.TabPanelItem} tab The tab being changed to
40234          */
40235         "beforetabchange" : true
40236     });
40237
40238     Roo.EventManager.onWindowResize(this.onResize, this);
40239     this.cpad = this.el.getPadding("lr");
40240     this.hiddenCount = 0;
40241
40242
40243     // toolbar on the tabbar support...
40244     if (this.toolbar) {
40245         alert("no toolbar support yet");
40246         this.toolbar  = false;
40247         /*
40248         var tcfg = this.toolbar;
40249         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40250         this.toolbar = new Roo.Toolbar(tcfg);
40251         if (Roo.isSafari) {
40252             var tbl = tcfg.container.child('table', true);
40253             tbl.setAttribute('width', '100%');
40254         }
40255         */
40256         
40257     }
40258    
40259
40260
40261     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40262 };
40263
40264 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40265     /*
40266      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40267      */
40268     tabPosition : "top",
40269     /*
40270      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40271      */
40272     currentTabWidth : 0,
40273     /*
40274      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40275      */
40276     minTabWidth : 40,
40277     /*
40278      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40279      */
40280     maxTabWidth : 250,
40281     /*
40282      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40283      */
40284     preferredTabWidth : 175,
40285     /*
40286      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40287      */
40288     resizeTabs : false,
40289     /*
40290      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40291      */
40292     monitorResize : true,
40293     /*
40294      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40295      */
40296     toolbar : false,  // set by caller..
40297     
40298     region : false, /// set by caller
40299     
40300     disableTooltips : true, // not used yet...
40301
40302     /**
40303      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40304      * @param {String} id The id of the div to use <b>or create</b>
40305      * @param {String} text The text for the tab
40306      * @param {String} content (optional) Content to put in the TabPanelItem body
40307      * @param {Boolean} closable (optional) True to create a close icon on the tab
40308      * @return {Roo.TabPanelItem} The created TabPanelItem
40309      */
40310     addTab : function(id, text, content, closable, tpl)
40311     {
40312         var item = new Roo.bootstrap.panel.TabItem({
40313             panel: this,
40314             id : id,
40315             text : text,
40316             closable : closable,
40317             tpl : tpl
40318         });
40319         this.addTabItem(item);
40320         if(content){
40321             item.setContent(content);
40322         }
40323         return item;
40324     },
40325
40326     /**
40327      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40328      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40329      * @return {Roo.TabPanelItem}
40330      */
40331     getTab : function(id){
40332         return this.items[id];
40333     },
40334
40335     /**
40336      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40337      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40338      */
40339     hideTab : function(id){
40340         var t = this.items[id];
40341         if(!t.isHidden()){
40342            t.setHidden(true);
40343            this.hiddenCount++;
40344            this.autoSizeTabs();
40345         }
40346     },
40347
40348     /**
40349      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40350      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40351      */
40352     unhideTab : function(id){
40353         var t = this.items[id];
40354         if(t.isHidden()){
40355            t.setHidden(false);
40356            this.hiddenCount--;
40357            this.autoSizeTabs();
40358         }
40359     },
40360
40361     /**
40362      * Adds an existing {@link Roo.TabPanelItem}.
40363      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40364      */
40365     addTabItem : function(item)
40366     {
40367         this.items[item.id] = item;
40368         this.items.push(item);
40369         this.autoSizeTabs();
40370       //  if(this.resizeTabs){
40371     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40372   //         this.autoSizeTabs();
40373 //        }else{
40374 //            item.autoSize();
40375        // }
40376     },
40377
40378     /**
40379      * Removes a {@link Roo.TabPanelItem}.
40380      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40381      */
40382     removeTab : function(id){
40383         var items = this.items;
40384         var tab = items[id];
40385         if(!tab) { return; }
40386         var index = items.indexOf(tab);
40387         if(this.active == tab && items.length > 1){
40388             var newTab = this.getNextAvailable(index);
40389             if(newTab) {
40390                 newTab.activate();
40391             }
40392         }
40393         this.stripEl.dom.removeChild(tab.pnode.dom);
40394         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40395             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40396         }
40397         items.splice(index, 1);
40398         delete this.items[tab.id];
40399         tab.fireEvent("close", tab);
40400         tab.purgeListeners();
40401         this.autoSizeTabs();
40402     },
40403
40404     getNextAvailable : function(start){
40405         var items = this.items;
40406         var index = start;
40407         // look for a next tab that will slide over to
40408         // replace the one being removed
40409         while(index < items.length){
40410             var item = items[++index];
40411             if(item && !item.isHidden()){
40412                 return item;
40413             }
40414         }
40415         // if one isn't found select the previous tab (on the left)
40416         index = start;
40417         while(index >= 0){
40418             var item = items[--index];
40419             if(item && !item.isHidden()){
40420                 return item;
40421             }
40422         }
40423         return null;
40424     },
40425
40426     /**
40427      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40428      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40429      */
40430     disableTab : function(id){
40431         var tab = this.items[id];
40432         if(tab && this.active != tab){
40433             tab.disable();
40434         }
40435     },
40436
40437     /**
40438      * Enables a {@link Roo.TabPanelItem} that is disabled.
40439      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40440      */
40441     enableTab : function(id){
40442         var tab = this.items[id];
40443         tab.enable();
40444     },
40445
40446     /**
40447      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40448      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40449      * @return {Roo.TabPanelItem} The TabPanelItem.
40450      */
40451     activate : function(id)
40452     {
40453         //Roo.log('activite:'  + id);
40454         
40455         var tab = this.items[id];
40456         if(!tab){
40457             return null;
40458         }
40459         if(tab == this.active || tab.disabled){
40460             return tab;
40461         }
40462         var e = {};
40463         this.fireEvent("beforetabchange", this, e, tab);
40464         if(e.cancel !== true && !tab.disabled){
40465             if(this.active){
40466                 this.active.hide();
40467             }
40468             this.active = this.items[id];
40469             this.active.show();
40470             this.fireEvent("tabchange", this, this.active);
40471         }
40472         return tab;
40473     },
40474
40475     /**
40476      * Gets the active {@link Roo.TabPanelItem}.
40477      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40478      */
40479     getActiveTab : function(){
40480         return this.active;
40481     },
40482
40483     /**
40484      * Updates the tab body element to fit the height of the container element
40485      * for overflow scrolling
40486      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40487      */
40488     syncHeight : function(targetHeight){
40489         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40490         var bm = this.bodyEl.getMargins();
40491         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40492         this.bodyEl.setHeight(newHeight);
40493         return newHeight;
40494     },
40495
40496     onResize : function(){
40497         if(this.monitorResize){
40498             this.autoSizeTabs();
40499         }
40500     },
40501
40502     /**
40503      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40504      */
40505     beginUpdate : function(){
40506         this.updating = true;
40507     },
40508
40509     /**
40510      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40511      */
40512     endUpdate : function(){
40513         this.updating = false;
40514         this.autoSizeTabs();
40515     },
40516
40517     /**
40518      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40519      */
40520     autoSizeTabs : function()
40521     {
40522         var count = this.items.length;
40523         var vcount = count - this.hiddenCount;
40524         
40525         if (vcount < 2) {
40526             this.stripEl.hide();
40527         } else {
40528             this.stripEl.show();
40529         }
40530         
40531         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40532             return;
40533         }
40534         
40535         
40536         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40537         var availWidth = Math.floor(w / vcount);
40538         var b = this.stripBody;
40539         if(b.getWidth() > w){
40540             var tabs = this.items;
40541             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40542             if(availWidth < this.minTabWidth){
40543                 /*if(!this.sleft){    // incomplete scrolling code
40544                     this.createScrollButtons();
40545                 }
40546                 this.showScroll();
40547                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40548             }
40549         }else{
40550             if(this.currentTabWidth < this.preferredTabWidth){
40551                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40552             }
40553         }
40554     },
40555
40556     /**
40557      * Returns the number of tabs in this TabPanel.
40558      * @return {Number}
40559      */
40560      getCount : function(){
40561          return this.items.length;
40562      },
40563
40564     /**
40565      * Resizes all the tabs to the passed width
40566      * @param {Number} The new width
40567      */
40568     setTabWidth : function(width){
40569         this.currentTabWidth = width;
40570         for(var i = 0, len = this.items.length; i < len; i++) {
40571                 if(!this.items[i].isHidden()) {
40572                 this.items[i].setWidth(width);
40573             }
40574         }
40575     },
40576
40577     /**
40578      * Destroys this TabPanel
40579      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40580      */
40581     destroy : function(removeEl){
40582         Roo.EventManager.removeResizeListener(this.onResize, this);
40583         for(var i = 0, len = this.items.length; i < len; i++){
40584             this.items[i].purgeListeners();
40585         }
40586         if(removeEl === true){
40587             this.el.update("");
40588             this.el.remove();
40589         }
40590     },
40591     
40592     createStrip : function(container)
40593     {
40594         var strip = document.createElement("nav");
40595         strip.className = Roo.bootstrap.version == 4 ?
40596             "navbar-light bg-light" : 
40597             "navbar navbar-default"; //"x-tabs-wrap";
40598         container.appendChild(strip);
40599         return strip;
40600     },
40601     
40602     createStripList : function(strip)
40603     {
40604         // div wrapper for retard IE
40605         // returns the "tr" element.
40606         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40607         //'<div class="x-tabs-strip-wrap">'+
40608           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40609           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40610         return strip.firstChild; //.firstChild.firstChild.firstChild;
40611     },
40612     createBody : function(container)
40613     {
40614         var body = document.createElement("div");
40615         Roo.id(body, "tab-body");
40616         //Roo.fly(body).addClass("x-tabs-body");
40617         Roo.fly(body).addClass("tab-content");
40618         container.appendChild(body);
40619         return body;
40620     },
40621     createItemBody :function(bodyEl, id){
40622         var body = Roo.getDom(id);
40623         if(!body){
40624             body = document.createElement("div");
40625             body.id = id;
40626         }
40627         //Roo.fly(body).addClass("x-tabs-item-body");
40628         Roo.fly(body).addClass("tab-pane");
40629          bodyEl.insertBefore(body, bodyEl.firstChild);
40630         return body;
40631     },
40632     /** @private */
40633     createStripElements :  function(stripEl, text, closable, tpl)
40634     {
40635         var td = document.createElement("li"); // was td..
40636         td.className = 'nav-item';
40637         
40638         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40639         
40640         
40641         stripEl.appendChild(td);
40642         /*if(closable){
40643             td.className = "x-tabs-closable";
40644             if(!this.closeTpl){
40645                 this.closeTpl = new Roo.Template(
40646                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40647                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40648                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
40649                 );
40650             }
40651             var el = this.closeTpl.overwrite(td, {"text": text});
40652             var close = el.getElementsByTagName("div")[0];
40653             var inner = el.getElementsByTagName("em")[0];
40654             return {"el": el, "close": close, "inner": inner};
40655         } else {
40656         */
40657         // not sure what this is..
40658 //            if(!this.tabTpl){
40659                 //this.tabTpl = new Roo.Template(
40660                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40661                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40662                 //);
40663 //                this.tabTpl = new Roo.Template(
40664 //                   '<a href="#">' +
40665 //                   '<span unselectable="on"' +
40666 //                            (this.disableTooltips ? '' : ' title="{text}"') +
40667 //                            ' >{text}</span></a>'
40668 //                );
40669 //                
40670 //            }
40671
40672
40673             var template = tpl || this.tabTpl || false;
40674             
40675             if(!template){
40676                 template =  new Roo.Template(
40677                         Roo.bootstrap.version == 4 ? 
40678                             (
40679                                 '<a class="nav-link" href="#" unselectable="on"' +
40680                                      (this.disableTooltips ? '' : ' title="{text}"') +
40681                                      ' >{text}</a>'
40682                             ) : (
40683                                 '<a class="nav-link" href="#">' +
40684                                 '<span unselectable="on"' +
40685                                          (this.disableTooltips ? '' : ' title="{text}"') +
40686                                     ' >{text}</span></a>'
40687                             )
40688                 );
40689             }
40690             
40691             switch (typeof(template)) {
40692                 case 'object' :
40693                     break;
40694                 case 'string' :
40695                     template = new Roo.Template(template);
40696                     break;
40697                 default :
40698                     break;
40699             }
40700             
40701             var el = template.overwrite(td, {"text": text});
40702             
40703             var inner = el.getElementsByTagName("span")[0];
40704             
40705             return {"el": el, "inner": inner};
40706             
40707     }
40708         
40709     
40710 });
40711
40712 /**
40713  * @class Roo.TabPanelItem
40714  * @extends Roo.util.Observable
40715  * Represents an individual item (tab plus body) in a TabPanel.
40716  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40717  * @param {String} id The id of this TabPanelItem
40718  * @param {String} text The text for the tab of this TabPanelItem
40719  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40720  */
40721 Roo.bootstrap.panel.TabItem = function(config){
40722     /**
40723      * The {@link Roo.TabPanel} this TabPanelItem belongs to
40724      * @type Roo.TabPanel
40725      */
40726     this.tabPanel = config.panel;
40727     /**
40728      * The id for this TabPanelItem
40729      * @type String
40730      */
40731     this.id = config.id;
40732     /** @private */
40733     this.disabled = false;
40734     /** @private */
40735     this.text = config.text;
40736     /** @private */
40737     this.loaded = false;
40738     this.closable = config.closable;
40739
40740     /**
40741      * The body element for this TabPanelItem.
40742      * @type Roo.Element
40743      */
40744     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
40745     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
40746     this.bodyEl.setStyle("display", "block");
40747     this.bodyEl.setStyle("zoom", "1");
40748     //this.hideAction();
40749
40750     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
40751     /** @private */
40752     this.el = Roo.get(els.el);
40753     this.inner = Roo.get(els.inner, true);
40754      this.textEl = Roo.bootstrap.version == 4 ?
40755         this.el : Roo.get(this.el.dom.firstChild, true);
40756
40757     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
40758     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
40759
40760     
40761 //    this.el.on("mousedown", this.onTabMouseDown, this);
40762     this.el.on("click", this.onTabClick, this);
40763     /** @private */
40764     if(config.closable){
40765         var c = Roo.get(els.close, true);
40766         c.dom.title = this.closeText;
40767         c.addClassOnOver("close-over");
40768         c.on("click", this.closeClick, this);
40769      }
40770
40771     this.addEvents({
40772          /**
40773          * @event activate
40774          * Fires when this tab becomes the active tab.
40775          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40776          * @param {Roo.TabPanelItem} this
40777          */
40778         "activate": true,
40779         /**
40780          * @event beforeclose
40781          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
40782          * @param {Roo.TabPanelItem} this
40783          * @param {Object} e Set cancel to true on this object to cancel the close.
40784          */
40785         "beforeclose": true,
40786         /**
40787          * @event close
40788          * Fires when this tab is closed.
40789          * @param {Roo.TabPanelItem} this
40790          */
40791          "close": true,
40792         /**
40793          * @event deactivate
40794          * Fires when this tab is no longer the active tab.
40795          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40796          * @param {Roo.TabPanelItem} this
40797          */
40798          "deactivate" : true
40799     });
40800     this.hidden = false;
40801
40802     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
40803 };
40804
40805 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
40806            {
40807     purgeListeners : function(){
40808        Roo.util.Observable.prototype.purgeListeners.call(this);
40809        this.el.removeAllListeners();
40810     },
40811     /**
40812      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
40813      */
40814     show : function(){
40815         this.status_node.addClass("active");
40816         this.showAction();
40817         if(Roo.isOpera){
40818             this.tabPanel.stripWrap.repaint();
40819         }
40820         this.fireEvent("activate", this.tabPanel, this);
40821     },
40822
40823     /**
40824      * Returns true if this tab is the active tab.
40825      * @return {Boolean}
40826      */
40827     isActive : function(){
40828         return this.tabPanel.getActiveTab() == this;
40829     },
40830
40831     /**
40832      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
40833      */
40834     hide : function(){
40835         this.status_node.removeClass("active");
40836         this.hideAction();
40837         this.fireEvent("deactivate", this.tabPanel, this);
40838     },
40839
40840     hideAction : function(){
40841         this.bodyEl.hide();
40842         this.bodyEl.setStyle("position", "absolute");
40843         this.bodyEl.setLeft("-20000px");
40844         this.bodyEl.setTop("-20000px");
40845     },
40846
40847     showAction : function(){
40848         this.bodyEl.setStyle("position", "relative");
40849         this.bodyEl.setTop("");
40850         this.bodyEl.setLeft("");
40851         this.bodyEl.show();
40852     },
40853
40854     /**
40855      * Set the tooltip for the tab.
40856      * @param {String} tooltip The tab's tooltip
40857      */
40858     setTooltip : function(text){
40859         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
40860             this.textEl.dom.qtip = text;
40861             this.textEl.dom.removeAttribute('title');
40862         }else{
40863             this.textEl.dom.title = text;
40864         }
40865     },
40866
40867     onTabClick : function(e){
40868         e.preventDefault();
40869         this.tabPanel.activate(this.id);
40870     },
40871
40872     onTabMouseDown : function(e){
40873         e.preventDefault();
40874         this.tabPanel.activate(this.id);
40875     },
40876 /*
40877     getWidth : function(){
40878         return this.inner.getWidth();
40879     },
40880
40881     setWidth : function(width){
40882         var iwidth = width - this.linode.getPadding("lr");
40883         this.inner.setWidth(iwidth);
40884         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
40885         this.linode.setWidth(width);
40886     },
40887 */
40888     /**
40889      * Show or hide the tab
40890      * @param {Boolean} hidden True to hide or false to show.
40891      */
40892     setHidden : function(hidden){
40893         this.hidden = hidden;
40894         this.linode.setStyle("display", hidden ? "none" : "");
40895     },
40896
40897     /**
40898      * Returns true if this tab is "hidden"
40899      * @return {Boolean}
40900      */
40901     isHidden : function(){
40902         return this.hidden;
40903     },
40904
40905     /**
40906      * Returns the text for this tab
40907      * @return {String}
40908      */
40909     getText : function(){
40910         return this.text;
40911     },
40912     /*
40913     autoSize : function(){
40914         //this.el.beginMeasure();
40915         this.textEl.setWidth(1);
40916         /*
40917          *  #2804 [new] Tabs in Roojs
40918          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
40919          */
40920         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
40921         //this.el.endMeasure();
40922     //},
40923
40924     /**
40925      * Sets the text for the tab (Note: this also sets the tooltip text)
40926      * @param {String} text The tab's text and tooltip
40927      */
40928     setText : function(text){
40929         this.text = text;
40930         this.textEl.update(text);
40931         this.setTooltip(text);
40932         //if(!this.tabPanel.resizeTabs){
40933         //    this.autoSize();
40934         //}
40935     },
40936     /**
40937      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
40938      */
40939     activate : function(){
40940         this.tabPanel.activate(this.id);
40941     },
40942
40943     /**
40944      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
40945      */
40946     disable : function(){
40947         if(this.tabPanel.active != this){
40948             this.disabled = true;
40949             this.status_node.addClass("disabled");
40950         }
40951     },
40952
40953     /**
40954      * Enables this TabPanelItem if it was previously disabled.
40955      */
40956     enable : function(){
40957         this.disabled = false;
40958         this.status_node.removeClass("disabled");
40959     },
40960
40961     /**
40962      * Sets the content for this TabPanelItem.
40963      * @param {String} content The content
40964      * @param {Boolean} loadScripts true to look for and load scripts
40965      */
40966     setContent : function(content, loadScripts){
40967         this.bodyEl.update(content, loadScripts);
40968     },
40969
40970     /**
40971      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
40972      * @return {Roo.UpdateManager} The UpdateManager
40973      */
40974     getUpdateManager : function(){
40975         return this.bodyEl.getUpdateManager();
40976     },
40977
40978     /**
40979      * Set a URL to be used to load the content for this TabPanelItem.
40980      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
40981      * @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)
40982      * @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)
40983      * @return {Roo.UpdateManager} The UpdateManager
40984      */
40985     setUrl : function(url, params, loadOnce){
40986         if(this.refreshDelegate){
40987             this.un('activate', this.refreshDelegate);
40988         }
40989         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40990         this.on("activate", this.refreshDelegate);
40991         return this.bodyEl.getUpdateManager();
40992     },
40993
40994     /** @private */
40995     _handleRefresh : function(url, params, loadOnce){
40996         if(!loadOnce || !this.loaded){
40997             var updater = this.bodyEl.getUpdateManager();
40998             updater.update(url, params, this._setLoaded.createDelegate(this));
40999         }
41000     },
41001
41002     /**
41003      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41004      *   Will fail silently if the setUrl method has not been called.
41005      *   This does not activate the panel, just updates its content.
41006      */
41007     refresh : function(){
41008         if(this.refreshDelegate){
41009            this.loaded = false;
41010            this.refreshDelegate();
41011         }
41012     },
41013
41014     /** @private */
41015     _setLoaded : function(){
41016         this.loaded = true;
41017     },
41018
41019     /** @private */
41020     closeClick : function(e){
41021         var o = {};
41022         e.stopEvent();
41023         this.fireEvent("beforeclose", this, o);
41024         if(o.cancel !== true){
41025             this.tabPanel.removeTab(this.id);
41026         }
41027     },
41028     /**
41029      * The text displayed in the tooltip for the close icon.
41030      * @type String
41031      */
41032     closeText : "Close this tab"
41033 });
41034 /**
41035 *    This script refer to:
41036 *    Title: International Telephone Input
41037 *    Author: Jack O'Connor
41038 *    Code version:  v12.1.12
41039 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41040 **/
41041
41042 Roo.bootstrap.PhoneInputData = function() {
41043     var d = [
41044       [
41045         "Afghanistan (‫افغانستان‬‎)",
41046         "af",
41047         "93"
41048       ],
41049       [
41050         "Albania (Shqipëri)",
41051         "al",
41052         "355"
41053       ],
41054       [
41055         "Algeria (‫الجزائر‬‎)",
41056         "dz",
41057         "213"
41058       ],
41059       [
41060         "American Samoa",
41061         "as",
41062         "1684"
41063       ],
41064       [
41065         "Andorra",
41066         "ad",
41067         "376"
41068       ],
41069       [
41070         "Angola",
41071         "ao",
41072         "244"
41073       ],
41074       [
41075         "Anguilla",
41076         "ai",
41077         "1264"
41078       ],
41079       [
41080         "Antigua and Barbuda",
41081         "ag",
41082         "1268"
41083       ],
41084       [
41085         "Argentina",
41086         "ar",
41087         "54"
41088       ],
41089       [
41090         "Armenia (Հայաստան)",
41091         "am",
41092         "374"
41093       ],
41094       [
41095         "Aruba",
41096         "aw",
41097         "297"
41098       ],
41099       [
41100         "Australia",
41101         "au",
41102         "61",
41103         0
41104       ],
41105       [
41106         "Austria (Österreich)",
41107         "at",
41108         "43"
41109       ],
41110       [
41111         "Azerbaijan (Azərbaycan)",
41112         "az",
41113         "994"
41114       ],
41115       [
41116         "Bahamas",
41117         "bs",
41118         "1242"
41119       ],
41120       [
41121         "Bahrain (‫البحرين‬‎)",
41122         "bh",
41123         "973"
41124       ],
41125       [
41126         "Bangladesh (বাংলাদেশ)",
41127         "bd",
41128         "880"
41129       ],
41130       [
41131         "Barbados",
41132         "bb",
41133         "1246"
41134       ],
41135       [
41136         "Belarus (Беларусь)",
41137         "by",
41138         "375"
41139       ],
41140       [
41141         "Belgium (België)",
41142         "be",
41143         "32"
41144       ],
41145       [
41146         "Belize",
41147         "bz",
41148         "501"
41149       ],
41150       [
41151         "Benin (Bénin)",
41152         "bj",
41153         "229"
41154       ],
41155       [
41156         "Bermuda",
41157         "bm",
41158         "1441"
41159       ],
41160       [
41161         "Bhutan (འབྲུག)",
41162         "bt",
41163         "975"
41164       ],
41165       [
41166         "Bolivia",
41167         "bo",
41168         "591"
41169       ],
41170       [
41171         "Bosnia and Herzegovina (Босна и Херцеговина)",
41172         "ba",
41173         "387"
41174       ],
41175       [
41176         "Botswana",
41177         "bw",
41178         "267"
41179       ],
41180       [
41181         "Brazil (Brasil)",
41182         "br",
41183         "55"
41184       ],
41185       [
41186         "British Indian Ocean Territory",
41187         "io",
41188         "246"
41189       ],
41190       [
41191         "British Virgin Islands",
41192         "vg",
41193         "1284"
41194       ],
41195       [
41196         "Brunei",
41197         "bn",
41198         "673"
41199       ],
41200       [
41201         "Bulgaria (България)",
41202         "bg",
41203         "359"
41204       ],
41205       [
41206         "Burkina Faso",
41207         "bf",
41208         "226"
41209       ],
41210       [
41211         "Burundi (Uburundi)",
41212         "bi",
41213         "257"
41214       ],
41215       [
41216         "Cambodia (កម្ពុជា)",
41217         "kh",
41218         "855"
41219       ],
41220       [
41221         "Cameroon (Cameroun)",
41222         "cm",
41223         "237"
41224       ],
41225       [
41226         "Canada",
41227         "ca",
41228         "1",
41229         1,
41230         ["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"]
41231       ],
41232       [
41233         "Cape Verde (Kabu Verdi)",
41234         "cv",
41235         "238"
41236       ],
41237       [
41238         "Caribbean Netherlands",
41239         "bq",
41240         "599",
41241         1
41242       ],
41243       [
41244         "Cayman Islands",
41245         "ky",
41246         "1345"
41247       ],
41248       [
41249         "Central African Republic (République centrafricaine)",
41250         "cf",
41251         "236"
41252       ],
41253       [
41254         "Chad (Tchad)",
41255         "td",
41256         "235"
41257       ],
41258       [
41259         "Chile",
41260         "cl",
41261         "56"
41262       ],
41263       [
41264         "China (中国)",
41265         "cn",
41266         "86"
41267       ],
41268       [
41269         "Christmas Island",
41270         "cx",
41271         "61",
41272         2
41273       ],
41274       [
41275         "Cocos (Keeling) Islands",
41276         "cc",
41277         "61",
41278         1
41279       ],
41280       [
41281         "Colombia",
41282         "co",
41283         "57"
41284       ],
41285       [
41286         "Comoros (‫جزر القمر‬‎)",
41287         "km",
41288         "269"
41289       ],
41290       [
41291         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41292         "cd",
41293         "243"
41294       ],
41295       [
41296         "Congo (Republic) (Congo-Brazzaville)",
41297         "cg",
41298         "242"
41299       ],
41300       [
41301         "Cook Islands",
41302         "ck",
41303         "682"
41304       ],
41305       [
41306         "Costa Rica",
41307         "cr",
41308         "506"
41309       ],
41310       [
41311         "Côte d’Ivoire",
41312         "ci",
41313         "225"
41314       ],
41315       [
41316         "Croatia (Hrvatska)",
41317         "hr",
41318         "385"
41319       ],
41320       [
41321         "Cuba",
41322         "cu",
41323         "53"
41324       ],
41325       [
41326         "Curaçao",
41327         "cw",
41328         "599",
41329         0
41330       ],
41331       [
41332         "Cyprus (Κύπρος)",
41333         "cy",
41334         "357"
41335       ],
41336       [
41337         "Czech Republic (Česká republika)",
41338         "cz",
41339         "420"
41340       ],
41341       [
41342         "Denmark (Danmark)",
41343         "dk",
41344         "45"
41345       ],
41346       [
41347         "Djibouti",
41348         "dj",
41349         "253"
41350       ],
41351       [
41352         "Dominica",
41353         "dm",
41354         "1767"
41355       ],
41356       [
41357         "Dominican Republic (República Dominicana)",
41358         "do",
41359         "1",
41360         2,
41361         ["809", "829", "849"]
41362       ],
41363       [
41364         "Ecuador",
41365         "ec",
41366         "593"
41367       ],
41368       [
41369         "Egypt (‫مصر‬‎)",
41370         "eg",
41371         "20"
41372       ],
41373       [
41374         "El Salvador",
41375         "sv",
41376         "503"
41377       ],
41378       [
41379         "Equatorial Guinea (Guinea Ecuatorial)",
41380         "gq",
41381         "240"
41382       ],
41383       [
41384         "Eritrea",
41385         "er",
41386         "291"
41387       ],
41388       [
41389         "Estonia (Eesti)",
41390         "ee",
41391         "372"
41392       ],
41393       [
41394         "Ethiopia",
41395         "et",
41396         "251"
41397       ],
41398       [
41399         "Falkland Islands (Islas Malvinas)",
41400         "fk",
41401         "500"
41402       ],
41403       [
41404         "Faroe Islands (Føroyar)",
41405         "fo",
41406         "298"
41407       ],
41408       [
41409         "Fiji",
41410         "fj",
41411         "679"
41412       ],
41413       [
41414         "Finland (Suomi)",
41415         "fi",
41416         "358",
41417         0
41418       ],
41419       [
41420         "France",
41421         "fr",
41422         "33"
41423       ],
41424       [
41425         "French Guiana (Guyane française)",
41426         "gf",
41427         "594"
41428       ],
41429       [
41430         "French Polynesia (Polynésie française)",
41431         "pf",
41432         "689"
41433       ],
41434       [
41435         "Gabon",
41436         "ga",
41437         "241"
41438       ],
41439       [
41440         "Gambia",
41441         "gm",
41442         "220"
41443       ],
41444       [
41445         "Georgia (საქართველო)",
41446         "ge",
41447         "995"
41448       ],
41449       [
41450         "Germany (Deutschland)",
41451         "de",
41452         "49"
41453       ],
41454       [
41455         "Ghana (Gaana)",
41456         "gh",
41457         "233"
41458       ],
41459       [
41460         "Gibraltar",
41461         "gi",
41462         "350"
41463       ],
41464       [
41465         "Greece (Ελλάδα)",
41466         "gr",
41467         "30"
41468       ],
41469       [
41470         "Greenland (Kalaallit Nunaat)",
41471         "gl",
41472         "299"
41473       ],
41474       [
41475         "Grenada",
41476         "gd",
41477         "1473"
41478       ],
41479       [
41480         "Guadeloupe",
41481         "gp",
41482         "590",
41483         0
41484       ],
41485       [
41486         "Guam",
41487         "gu",
41488         "1671"
41489       ],
41490       [
41491         "Guatemala",
41492         "gt",
41493         "502"
41494       ],
41495       [
41496         "Guernsey",
41497         "gg",
41498         "44",
41499         1
41500       ],
41501       [
41502         "Guinea (Guinée)",
41503         "gn",
41504         "224"
41505       ],
41506       [
41507         "Guinea-Bissau (Guiné Bissau)",
41508         "gw",
41509         "245"
41510       ],
41511       [
41512         "Guyana",
41513         "gy",
41514         "592"
41515       ],
41516       [
41517         "Haiti",
41518         "ht",
41519         "509"
41520       ],
41521       [
41522         "Honduras",
41523         "hn",
41524         "504"
41525       ],
41526       [
41527         "Hong Kong (香港)",
41528         "hk",
41529         "852"
41530       ],
41531       [
41532         "Hungary (Magyarország)",
41533         "hu",
41534         "36"
41535       ],
41536       [
41537         "Iceland (Ísland)",
41538         "is",
41539         "354"
41540       ],
41541       [
41542         "India (भारत)",
41543         "in",
41544         "91"
41545       ],
41546       [
41547         "Indonesia",
41548         "id",
41549         "62"
41550       ],
41551       [
41552         "Iran (‫ایران‬‎)",
41553         "ir",
41554         "98"
41555       ],
41556       [
41557         "Iraq (‫العراق‬‎)",
41558         "iq",
41559         "964"
41560       ],
41561       [
41562         "Ireland",
41563         "ie",
41564         "353"
41565       ],
41566       [
41567         "Isle of Man",
41568         "im",
41569         "44",
41570         2
41571       ],
41572       [
41573         "Israel (‫ישראל‬‎)",
41574         "il",
41575         "972"
41576       ],
41577       [
41578         "Italy (Italia)",
41579         "it",
41580         "39",
41581         0
41582       ],
41583       [
41584         "Jamaica",
41585         "jm",
41586         "1876"
41587       ],
41588       [
41589         "Japan (日本)",
41590         "jp",
41591         "81"
41592       ],
41593       [
41594         "Jersey",
41595         "je",
41596         "44",
41597         3
41598       ],
41599       [
41600         "Jordan (‫الأردن‬‎)",
41601         "jo",
41602         "962"
41603       ],
41604       [
41605         "Kazakhstan (Казахстан)",
41606         "kz",
41607         "7",
41608         1
41609       ],
41610       [
41611         "Kenya",
41612         "ke",
41613         "254"
41614       ],
41615       [
41616         "Kiribati",
41617         "ki",
41618         "686"
41619       ],
41620       [
41621         "Kosovo",
41622         "xk",
41623         "383"
41624       ],
41625       [
41626         "Kuwait (‫الكويت‬‎)",
41627         "kw",
41628         "965"
41629       ],
41630       [
41631         "Kyrgyzstan (Кыргызстан)",
41632         "kg",
41633         "996"
41634       ],
41635       [
41636         "Laos (ລາວ)",
41637         "la",
41638         "856"
41639       ],
41640       [
41641         "Latvia (Latvija)",
41642         "lv",
41643         "371"
41644       ],
41645       [
41646         "Lebanon (‫لبنان‬‎)",
41647         "lb",
41648         "961"
41649       ],
41650       [
41651         "Lesotho",
41652         "ls",
41653         "266"
41654       ],
41655       [
41656         "Liberia",
41657         "lr",
41658         "231"
41659       ],
41660       [
41661         "Libya (‫ليبيا‬‎)",
41662         "ly",
41663         "218"
41664       ],
41665       [
41666         "Liechtenstein",
41667         "li",
41668         "423"
41669       ],
41670       [
41671         "Lithuania (Lietuva)",
41672         "lt",
41673         "370"
41674       ],
41675       [
41676         "Luxembourg",
41677         "lu",
41678         "352"
41679       ],
41680       [
41681         "Macau (澳門)",
41682         "mo",
41683         "853"
41684       ],
41685       [
41686         "Macedonia (FYROM) (Македонија)",
41687         "mk",
41688         "389"
41689       ],
41690       [
41691         "Madagascar (Madagasikara)",
41692         "mg",
41693         "261"
41694       ],
41695       [
41696         "Malawi",
41697         "mw",
41698         "265"
41699       ],
41700       [
41701         "Malaysia",
41702         "my",
41703         "60"
41704       ],
41705       [
41706         "Maldives",
41707         "mv",
41708         "960"
41709       ],
41710       [
41711         "Mali",
41712         "ml",
41713         "223"
41714       ],
41715       [
41716         "Malta",
41717         "mt",
41718         "356"
41719       ],
41720       [
41721         "Marshall Islands",
41722         "mh",
41723         "692"
41724       ],
41725       [
41726         "Martinique",
41727         "mq",
41728         "596"
41729       ],
41730       [
41731         "Mauritania (‫موريتانيا‬‎)",
41732         "mr",
41733         "222"
41734       ],
41735       [
41736         "Mauritius (Moris)",
41737         "mu",
41738         "230"
41739       ],
41740       [
41741         "Mayotte",
41742         "yt",
41743         "262",
41744         1
41745       ],
41746       [
41747         "Mexico (México)",
41748         "mx",
41749         "52"
41750       ],
41751       [
41752         "Micronesia",
41753         "fm",
41754         "691"
41755       ],
41756       [
41757         "Moldova (Republica Moldova)",
41758         "md",
41759         "373"
41760       ],
41761       [
41762         "Monaco",
41763         "mc",
41764         "377"
41765       ],
41766       [
41767         "Mongolia (Монгол)",
41768         "mn",
41769         "976"
41770       ],
41771       [
41772         "Montenegro (Crna Gora)",
41773         "me",
41774         "382"
41775       ],
41776       [
41777         "Montserrat",
41778         "ms",
41779         "1664"
41780       ],
41781       [
41782         "Morocco (‫المغرب‬‎)",
41783         "ma",
41784         "212",
41785         0
41786       ],
41787       [
41788         "Mozambique (Moçambique)",
41789         "mz",
41790         "258"
41791       ],
41792       [
41793         "Myanmar (Burma) (မြန်မာ)",
41794         "mm",
41795         "95"
41796       ],
41797       [
41798         "Namibia (Namibië)",
41799         "na",
41800         "264"
41801       ],
41802       [
41803         "Nauru",
41804         "nr",
41805         "674"
41806       ],
41807       [
41808         "Nepal (नेपाल)",
41809         "np",
41810         "977"
41811       ],
41812       [
41813         "Netherlands (Nederland)",
41814         "nl",
41815         "31"
41816       ],
41817       [
41818         "New Caledonia (Nouvelle-Calédonie)",
41819         "nc",
41820         "687"
41821       ],
41822       [
41823         "New Zealand",
41824         "nz",
41825         "64"
41826       ],
41827       [
41828         "Nicaragua",
41829         "ni",
41830         "505"
41831       ],
41832       [
41833         "Niger (Nijar)",
41834         "ne",
41835         "227"
41836       ],
41837       [
41838         "Nigeria",
41839         "ng",
41840         "234"
41841       ],
41842       [
41843         "Niue",
41844         "nu",
41845         "683"
41846       ],
41847       [
41848         "Norfolk Island",
41849         "nf",
41850         "672"
41851       ],
41852       [
41853         "North Korea (조선 민주주의 인민 공화국)",
41854         "kp",
41855         "850"
41856       ],
41857       [
41858         "Northern Mariana Islands",
41859         "mp",
41860         "1670"
41861       ],
41862       [
41863         "Norway (Norge)",
41864         "no",
41865         "47",
41866         0
41867       ],
41868       [
41869         "Oman (‫عُمان‬‎)",
41870         "om",
41871         "968"
41872       ],
41873       [
41874         "Pakistan (‫پاکستان‬‎)",
41875         "pk",
41876         "92"
41877       ],
41878       [
41879         "Palau",
41880         "pw",
41881         "680"
41882       ],
41883       [
41884         "Palestine (‫فلسطين‬‎)",
41885         "ps",
41886         "970"
41887       ],
41888       [
41889         "Panama (Panamá)",
41890         "pa",
41891         "507"
41892       ],
41893       [
41894         "Papua New Guinea",
41895         "pg",
41896         "675"
41897       ],
41898       [
41899         "Paraguay",
41900         "py",
41901         "595"
41902       ],
41903       [
41904         "Peru (Perú)",
41905         "pe",
41906         "51"
41907       ],
41908       [
41909         "Philippines",
41910         "ph",
41911         "63"
41912       ],
41913       [
41914         "Poland (Polska)",
41915         "pl",
41916         "48"
41917       ],
41918       [
41919         "Portugal",
41920         "pt",
41921         "351"
41922       ],
41923       [
41924         "Puerto Rico",
41925         "pr",
41926         "1",
41927         3,
41928         ["787", "939"]
41929       ],
41930       [
41931         "Qatar (‫قطر‬‎)",
41932         "qa",
41933         "974"
41934       ],
41935       [
41936         "Réunion (La Réunion)",
41937         "re",
41938         "262",
41939         0
41940       ],
41941       [
41942         "Romania (România)",
41943         "ro",
41944         "40"
41945       ],
41946       [
41947         "Russia (Россия)",
41948         "ru",
41949         "7",
41950         0
41951       ],
41952       [
41953         "Rwanda",
41954         "rw",
41955         "250"
41956       ],
41957       [
41958         "Saint Barthélemy",
41959         "bl",
41960         "590",
41961         1
41962       ],
41963       [
41964         "Saint Helena",
41965         "sh",
41966         "290"
41967       ],
41968       [
41969         "Saint Kitts and Nevis",
41970         "kn",
41971         "1869"
41972       ],
41973       [
41974         "Saint Lucia",
41975         "lc",
41976         "1758"
41977       ],
41978       [
41979         "Saint Martin (Saint-Martin (partie française))",
41980         "mf",
41981         "590",
41982         2
41983       ],
41984       [
41985         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
41986         "pm",
41987         "508"
41988       ],
41989       [
41990         "Saint Vincent and the Grenadines",
41991         "vc",
41992         "1784"
41993       ],
41994       [
41995         "Samoa",
41996         "ws",
41997         "685"
41998       ],
41999       [
42000         "San Marino",
42001         "sm",
42002         "378"
42003       ],
42004       [
42005         "São Tomé and Príncipe (São Tomé e Príncipe)",
42006         "st",
42007         "239"
42008       ],
42009       [
42010         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42011         "sa",
42012         "966"
42013       ],
42014       [
42015         "Senegal (Sénégal)",
42016         "sn",
42017         "221"
42018       ],
42019       [
42020         "Serbia (Србија)",
42021         "rs",
42022         "381"
42023       ],
42024       [
42025         "Seychelles",
42026         "sc",
42027         "248"
42028       ],
42029       [
42030         "Sierra Leone",
42031         "sl",
42032         "232"
42033       ],
42034       [
42035         "Singapore",
42036         "sg",
42037         "65"
42038       ],
42039       [
42040         "Sint Maarten",
42041         "sx",
42042         "1721"
42043       ],
42044       [
42045         "Slovakia (Slovensko)",
42046         "sk",
42047         "421"
42048       ],
42049       [
42050         "Slovenia (Slovenija)",
42051         "si",
42052         "386"
42053       ],
42054       [
42055         "Solomon Islands",
42056         "sb",
42057         "677"
42058       ],
42059       [
42060         "Somalia (Soomaaliya)",
42061         "so",
42062         "252"
42063       ],
42064       [
42065         "South Africa",
42066         "za",
42067         "27"
42068       ],
42069       [
42070         "South Korea (대한민국)",
42071         "kr",
42072         "82"
42073       ],
42074       [
42075         "South Sudan (‫جنوب السودان‬‎)",
42076         "ss",
42077         "211"
42078       ],
42079       [
42080         "Spain (España)",
42081         "es",
42082         "34"
42083       ],
42084       [
42085         "Sri Lanka (ශ්‍රී ලංකාව)",
42086         "lk",
42087         "94"
42088       ],
42089       [
42090         "Sudan (‫السودان‬‎)",
42091         "sd",
42092         "249"
42093       ],
42094       [
42095         "Suriname",
42096         "sr",
42097         "597"
42098       ],
42099       [
42100         "Svalbard and Jan Mayen",
42101         "sj",
42102         "47",
42103         1
42104       ],
42105       [
42106         "Swaziland",
42107         "sz",
42108         "268"
42109       ],
42110       [
42111         "Sweden (Sverige)",
42112         "se",
42113         "46"
42114       ],
42115       [
42116         "Switzerland (Schweiz)",
42117         "ch",
42118         "41"
42119       ],
42120       [
42121         "Syria (‫سوريا‬‎)",
42122         "sy",
42123         "963"
42124       ],
42125       [
42126         "Taiwan (台灣)",
42127         "tw",
42128         "886"
42129       ],
42130       [
42131         "Tajikistan",
42132         "tj",
42133         "992"
42134       ],
42135       [
42136         "Tanzania",
42137         "tz",
42138         "255"
42139       ],
42140       [
42141         "Thailand (ไทย)",
42142         "th",
42143         "66"
42144       ],
42145       [
42146         "Timor-Leste",
42147         "tl",
42148         "670"
42149       ],
42150       [
42151         "Togo",
42152         "tg",
42153         "228"
42154       ],
42155       [
42156         "Tokelau",
42157         "tk",
42158         "690"
42159       ],
42160       [
42161         "Tonga",
42162         "to",
42163         "676"
42164       ],
42165       [
42166         "Trinidad and Tobago",
42167         "tt",
42168         "1868"
42169       ],
42170       [
42171         "Tunisia (‫تونس‬‎)",
42172         "tn",
42173         "216"
42174       ],
42175       [
42176         "Turkey (Türkiye)",
42177         "tr",
42178         "90"
42179       ],
42180       [
42181         "Turkmenistan",
42182         "tm",
42183         "993"
42184       ],
42185       [
42186         "Turks and Caicos Islands",
42187         "tc",
42188         "1649"
42189       ],
42190       [
42191         "Tuvalu",
42192         "tv",
42193         "688"
42194       ],
42195       [
42196         "U.S. Virgin Islands",
42197         "vi",
42198         "1340"
42199       ],
42200       [
42201         "Uganda",
42202         "ug",
42203         "256"
42204       ],
42205       [
42206         "Ukraine (Україна)",
42207         "ua",
42208         "380"
42209       ],
42210       [
42211         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42212         "ae",
42213         "971"
42214       ],
42215       [
42216         "United Kingdom",
42217         "gb",
42218         "44",
42219         0
42220       ],
42221       [
42222         "United States",
42223         "us",
42224         "1",
42225         0
42226       ],
42227       [
42228         "Uruguay",
42229         "uy",
42230         "598"
42231       ],
42232       [
42233         "Uzbekistan (Oʻzbekiston)",
42234         "uz",
42235         "998"
42236       ],
42237       [
42238         "Vanuatu",
42239         "vu",
42240         "678"
42241       ],
42242       [
42243         "Vatican City (Città del Vaticano)",
42244         "va",
42245         "39",
42246         1
42247       ],
42248       [
42249         "Venezuela",
42250         "ve",
42251         "58"
42252       ],
42253       [
42254         "Vietnam (Việt Nam)",
42255         "vn",
42256         "84"
42257       ],
42258       [
42259         "Wallis and Futuna (Wallis-et-Futuna)",
42260         "wf",
42261         "681"
42262       ],
42263       [
42264         "Western Sahara (‫الصحراء الغربية‬‎)",
42265         "eh",
42266         "212",
42267         1
42268       ],
42269       [
42270         "Yemen (‫اليمن‬‎)",
42271         "ye",
42272         "967"
42273       ],
42274       [
42275         "Zambia",
42276         "zm",
42277         "260"
42278       ],
42279       [
42280         "Zimbabwe",
42281         "zw",
42282         "263"
42283       ],
42284       [
42285         "Åland Islands",
42286         "ax",
42287         "358",
42288         1
42289       ]
42290   ];
42291   
42292   return d;
42293 }/**
42294 *    This script refer to:
42295 *    Title: International Telephone Input
42296 *    Author: Jack O'Connor
42297 *    Code version:  v12.1.12
42298 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42299 **/
42300
42301 /**
42302  * @class Roo.bootstrap.PhoneInput
42303  * @extends Roo.bootstrap.TriggerField
42304  * An input with International dial-code selection
42305  
42306  * @cfg {String} defaultDialCode default '+852'
42307  * @cfg {Array} preferedCountries default []
42308   
42309  * @constructor
42310  * Create a new PhoneInput.
42311  * @param {Object} config Configuration options
42312  */
42313
42314 Roo.bootstrap.PhoneInput = function(config) {
42315     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42316 };
42317
42318 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42319         
42320         listWidth: undefined,
42321         
42322         selectedClass: 'active',
42323         
42324         invalidClass : "has-warning",
42325         
42326         validClass: 'has-success',
42327         
42328         allowed: '0123456789',
42329         
42330         max_length: 15,
42331         
42332         /**
42333          * @cfg {String} defaultDialCode The default dial code when initializing the input
42334          */
42335         defaultDialCode: '+852',
42336         
42337         /**
42338          * @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
42339          */
42340         preferedCountries: false,
42341         
42342         getAutoCreate : function()
42343         {
42344             var data = Roo.bootstrap.PhoneInputData();
42345             var align = this.labelAlign || this.parentLabelAlign();
42346             var id = Roo.id();
42347             
42348             this.allCountries = [];
42349             this.dialCodeMapping = [];
42350             
42351             for (var i = 0; i < data.length; i++) {
42352               var c = data[i];
42353               this.allCountries[i] = {
42354                 name: c[0],
42355                 iso2: c[1],
42356                 dialCode: c[2],
42357                 priority: c[3] || 0,
42358                 areaCodes: c[4] || null
42359               };
42360               this.dialCodeMapping[c[2]] = {
42361                   name: c[0],
42362                   iso2: c[1],
42363                   priority: c[3] || 0,
42364                   areaCodes: c[4] || null
42365               };
42366             }
42367             
42368             var cfg = {
42369                 cls: 'form-group',
42370                 cn: []
42371             };
42372             
42373             var input =  {
42374                 tag: 'input',
42375                 id : id,
42376                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42377                 maxlength: this.max_length,
42378                 cls : 'form-control tel-input',
42379                 autocomplete: 'new-password'
42380             };
42381             
42382             var hiddenInput = {
42383                 tag: 'input',
42384                 type: 'hidden',
42385                 cls: 'hidden-tel-input'
42386             };
42387             
42388             if (this.name) {
42389                 hiddenInput.name = this.name;
42390             }
42391             
42392             if (this.disabled) {
42393                 input.disabled = true;
42394             }
42395             
42396             var flag_container = {
42397                 tag: 'div',
42398                 cls: 'flag-box',
42399                 cn: [
42400                     {
42401                         tag: 'div',
42402                         cls: 'flag'
42403                     },
42404                     {
42405                         tag: 'div',
42406                         cls: 'caret'
42407                     }
42408                 ]
42409             };
42410             
42411             var box = {
42412                 tag: 'div',
42413                 cls: this.hasFeedback ? 'has-feedback' : '',
42414                 cn: [
42415                     hiddenInput,
42416                     input,
42417                     {
42418                         tag: 'input',
42419                         cls: 'dial-code-holder',
42420                         disabled: true
42421                     }
42422                 ]
42423             };
42424             
42425             var container = {
42426                 cls: 'roo-select2-container input-group',
42427                 cn: [
42428                     flag_container,
42429                     box
42430                 ]
42431             };
42432             
42433             if (this.fieldLabel.length) {
42434                 var indicator = {
42435                     tag: 'i',
42436                     tooltip: 'This field is required'
42437                 };
42438                 
42439                 var label = {
42440                     tag: 'label',
42441                     'for':  id,
42442                     cls: 'control-label',
42443                     cn: []
42444                 };
42445                 
42446                 var label_text = {
42447                     tag: 'span',
42448                     html: this.fieldLabel
42449                 };
42450                 
42451                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42452                 label.cn = [
42453                     indicator,
42454                     label_text
42455                 ];
42456                 
42457                 if(this.indicatorpos == 'right') {
42458                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42459                     label.cn = [
42460                         label_text,
42461                         indicator
42462                     ];
42463                 }
42464                 
42465                 if(align == 'left') {
42466                     container = {
42467                         tag: 'div',
42468                         cn: [
42469                             container
42470                         ]
42471                     };
42472                     
42473                     if(this.labelWidth > 12){
42474                         label.style = "width: " + this.labelWidth + 'px';
42475                     }
42476                     if(this.labelWidth < 13 && this.labelmd == 0){
42477                         this.labelmd = this.labelWidth;
42478                     }
42479                     if(this.labellg > 0){
42480                         label.cls += ' col-lg-' + this.labellg;
42481                         input.cls += ' col-lg-' + (12 - this.labellg);
42482                     }
42483                     if(this.labelmd > 0){
42484                         label.cls += ' col-md-' + this.labelmd;
42485                         container.cls += ' col-md-' + (12 - this.labelmd);
42486                     }
42487                     if(this.labelsm > 0){
42488                         label.cls += ' col-sm-' + this.labelsm;
42489                         container.cls += ' col-sm-' + (12 - this.labelsm);
42490                     }
42491                     if(this.labelxs > 0){
42492                         label.cls += ' col-xs-' + this.labelxs;
42493                         container.cls += ' col-xs-' + (12 - this.labelxs);
42494                     }
42495                 }
42496             }
42497             
42498             cfg.cn = [
42499                 label,
42500                 container
42501             ];
42502             
42503             var settings = this;
42504             
42505             ['xs','sm','md','lg'].map(function(size){
42506                 if (settings[size]) {
42507                     cfg.cls += ' col-' + size + '-' + settings[size];
42508                 }
42509             });
42510             
42511             this.store = new Roo.data.Store({
42512                 proxy : new Roo.data.MemoryProxy({}),
42513                 reader : new Roo.data.JsonReader({
42514                     fields : [
42515                         {
42516                             'name' : 'name',
42517                             'type' : 'string'
42518                         },
42519                         {
42520                             'name' : 'iso2',
42521                             'type' : 'string'
42522                         },
42523                         {
42524                             'name' : 'dialCode',
42525                             'type' : 'string'
42526                         },
42527                         {
42528                             'name' : 'priority',
42529                             'type' : 'string'
42530                         },
42531                         {
42532                             'name' : 'areaCodes',
42533                             'type' : 'string'
42534                         }
42535                     ]
42536                 })
42537             });
42538             
42539             if(!this.preferedCountries) {
42540                 this.preferedCountries = [
42541                     'hk',
42542                     'gb',
42543                     'us'
42544                 ];
42545             }
42546             
42547             var p = this.preferedCountries.reverse();
42548             
42549             if(p) {
42550                 for (var i = 0; i < p.length; i++) {
42551                     for (var j = 0; j < this.allCountries.length; j++) {
42552                         if(this.allCountries[j].iso2 == p[i]) {
42553                             var t = this.allCountries[j];
42554                             this.allCountries.splice(j,1);
42555                             this.allCountries.unshift(t);
42556                         }
42557                     } 
42558                 }
42559             }
42560             
42561             this.store.proxy.data = {
42562                 success: true,
42563                 data: this.allCountries
42564             };
42565             
42566             return cfg;
42567         },
42568         
42569         initEvents : function()
42570         {
42571             this.createList();
42572             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42573             
42574             this.indicator = this.indicatorEl();
42575             this.flag = this.flagEl();
42576             this.dialCodeHolder = this.dialCodeHolderEl();
42577             
42578             this.trigger = this.el.select('div.flag-box',true).first();
42579             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42580             
42581             var _this = this;
42582             
42583             (function(){
42584                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42585                 _this.list.setWidth(lw);
42586             }).defer(100);
42587             
42588             this.list.on('mouseover', this.onViewOver, this);
42589             this.list.on('mousemove', this.onViewMove, this);
42590             this.inputEl().on("keyup", this.onKeyUp, this);
42591             this.inputEl().on("keypress", this.onKeyPress, this);
42592             
42593             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42594
42595             this.view = new Roo.View(this.list, this.tpl, {
42596                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42597             });
42598             
42599             this.view.on('click', this.onViewClick, this);
42600             this.setValue(this.defaultDialCode);
42601         },
42602         
42603         onTriggerClick : function(e)
42604         {
42605             Roo.log('trigger click');
42606             if(this.disabled){
42607                 return;
42608             }
42609             
42610             if(this.isExpanded()){
42611                 this.collapse();
42612                 this.hasFocus = false;
42613             }else {
42614                 this.store.load({});
42615                 this.hasFocus = true;
42616                 this.expand();
42617             }
42618         },
42619         
42620         isExpanded : function()
42621         {
42622             return this.list.isVisible();
42623         },
42624         
42625         collapse : function()
42626         {
42627             if(!this.isExpanded()){
42628                 return;
42629             }
42630             this.list.hide();
42631             Roo.get(document).un('mousedown', this.collapseIf, this);
42632             Roo.get(document).un('mousewheel', this.collapseIf, this);
42633             this.fireEvent('collapse', this);
42634             this.validate();
42635         },
42636         
42637         expand : function()
42638         {
42639             Roo.log('expand');
42640
42641             if(this.isExpanded() || !this.hasFocus){
42642                 return;
42643             }
42644             
42645             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42646             this.list.setWidth(lw);
42647             
42648             this.list.show();
42649             this.restrictHeight();
42650             
42651             Roo.get(document).on('mousedown', this.collapseIf, this);
42652             Roo.get(document).on('mousewheel', this.collapseIf, this);
42653             
42654             this.fireEvent('expand', this);
42655         },
42656         
42657         restrictHeight : function()
42658         {
42659             this.list.alignTo(this.inputEl(), this.listAlign);
42660             this.list.alignTo(this.inputEl(), this.listAlign);
42661         },
42662         
42663         onViewOver : function(e, t)
42664         {
42665             if(this.inKeyMode){
42666                 return;
42667             }
42668             var item = this.view.findItemFromChild(t);
42669             
42670             if(item){
42671                 var index = this.view.indexOf(item);
42672                 this.select(index, false);
42673             }
42674         },
42675
42676         // private
42677         onViewClick : function(view, doFocus, el, e)
42678         {
42679             var index = this.view.getSelectedIndexes()[0];
42680             
42681             var r = this.store.getAt(index);
42682             
42683             if(r){
42684                 this.onSelect(r, index);
42685             }
42686             if(doFocus !== false && !this.blockFocus){
42687                 this.inputEl().focus();
42688             }
42689         },
42690         
42691         onViewMove : function(e, t)
42692         {
42693             this.inKeyMode = false;
42694         },
42695         
42696         select : function(index, scrollIntoView)
42697         {
42698             this.selectedIndex = index;
42699             this.view.select(index);
42700             if(scrollIntoView !== false){
42701                 var el = this.view.getNode(index);
42702                 if(el){
42703                     this.list.scrollChildIntoView(el, false);
42704                 }
42705             }
42706         },
42707         
42708         createList : function()
42709         {
42710             this.list = Roo.get(document.body).createChild({
42711                 tag: 'ul',
42712                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42713                 style: 'display:none'
42714             });
42715             
42716             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42717         },
42718         
42719         collapseIf : function(e)
42720         {
42721             var in_combo  = e.within(this.el);
42722             var in_list =  e.within(this.list);
42723             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42724             
42725             if (in_combo || in_list || is_list) {
42726                 return;
42727             }
42728             this.collapse();
42729         },
42730         
42731         onSelect : function(record, index)
42732         {
42733             if(this.fireEvent('beforeselect', this, record, index) !== false){
42734                 
42735                 this.setFlagClass(record.data.iso2);
42736                 this.setDialCode(record.data.dialCode);
42737                 this.hasFocus = false;
42738                 this.collapse();
42739                 this.fireEvent('select', this, record, index);
42740             }
42741         },
42742         
42743         flagEl : function()
42744         {
42745             var flag = this.el.select('div.flag',true).first();
42746             if(!flag){
42747                 return false;
42748             }
42749             return flag;
42750         },
42751         
42752         dialCodeHolderEl : function()
42753         {
42754             var d = this.el.select('input.dial-code-holder',true).first();
42755             if(!d){
42756                 return false;
42757             }
42758             return d;
42759         },
42760         
42761         setDialCode : function(v)
42762         {
42763             this.dialCodeHolder.dom.value = '+'+v;
42764         },
42765         
42766         setFlagClass : function(n)
42767         {
42768             this.flag.dom.className = 'flag '+n;
42769         },
42770         
42771         getValue : function()
42772         {
42773             var v = this.inputEl().getValue();
42774             if(this.dialCodeHolder) {
42775                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
42776             }
42777             return v;
42778         },
42779         
42780         setValue : function(v)
42781         {
42782             var d = this.getDialCode(v);
42783             
42784             //invalid dial code
42785             if(v.length == 0 || !d || d.length == 0) {
42786                 if(this.rendered){
42787                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42788                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42789                 }
42790                 return;
42791             }
42792             
42793             //valid dial code
42794             this.setFlagClass(this.dialCodeMapping[d].iso2);
42795             this.setDialCode(d);
42796             this.inputEl().dom.value = v.replace('+'+d,'');
42797             this.hiddenEl().dom.value = this.getValue();
42798             
42799             this.validate();
42800         },
42801         
42802         getDialCode : function(v)
42803         {
42804             v = v ||  '';
42805             
42806             if (v.length == 0) {
42807                 return this.dialCodeHolder.dom.value;
42808             }
42809             
42810             var dialCode = "";
42811             if (v.charAt(0) != "+") {
42812                 return false;
42813             }
42814             var numericChars = "";
42815             for (var i = 1; i < v.length; i++) {
42816               var c = v.charAt(i);
42817               if (!isNaN(c)) {
42818                 numericChars += c;
42819                 if (this.dialCodeMapping[numericChars]) {
42820                   dialCode = v.substr(1, i);
42821                 }
42822                 if (numericChars.length == 4) {
42823                   break;
42824                 }
42825               }
42826             }
42827             return dialCode;
42828         },
42829         
42830         reset : function()
42831         {
42832             this.setValue(this.defaultDialCode);
42833             this.validate();
42834         },
42835         
42836         hiddenEl : function()
42837         {
42838             return this.el.select('input.hidden-tel-input',true).first();
42839         },
42840         
42841         // after setting val
42842         onKeyUp : function(e){
42843             this.setValue(this.getValue());
42844         },
42845         
42846         onKeyPress : function(e){
42847             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
42848                 e.stopEvent();
42849             }
42850         }
42851         
42852 });
42853 /**
42854  * @class Roo.bootstrap.MoneyField
42855  * @extends Roo.bootstrap.ComboBox
42856  * Bootstrap MoneyField class
42857  * 
42858  * @constructor
42859  * Create a new MoneyField.
42860  * @param {Object} config Configuration options
42861  */
42862
42863 Roo.bootstrap.MoneyField = function(config) {
42864     
42865     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
42866     
42867 };
42868
42869 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
42870     
42871     /**
42872      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
42873      */
42874     allowDecimals : true,
42875     /**
42876      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
42877      */
42878     decimalSeparator : ".",
42879     /**
42880      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
42881      */
42882     decimalPrecision : 0,
42883     /**
42884      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
42885      */
42886     allowNegative : true,
42887     /**
42888      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
42889      */
42890     allowZero: true,
42891     /**
42892      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
42893      */
42894     minValue : Number.NEGATIVE_INFINITY,
42895     /**
42896      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
42897      */
42898     maxValue : Number.MAX_VALUE,
42899     /**
42900      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
42901      */
42902     minText : "The minimum value for this field is {0}",
42903     /**
42904      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
42905      */
42906     maxText : "The maximum value for this field is {0}",
42907     /**
42908      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
42909      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
42910      */
42911     nanText : "{0} is not a valid number",
42912     /**
42913      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
42914      */
42915     castInt : true,
42916     /**
42917      * @cfg {String} defaults currency of the MoneyField
42918      * value should be in lkey
42919      */
42920     defaultCurrency : false,
42921     /**
42922      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
42923      */
42924     thousandsDelimiter : false,
42925     /**
42926      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
42927      */
42928     max_length: false,
42929     
42930     inputlg : 9,
42931     inputmd : 9,
42932     inputsm : 9,
42933     inputxs : 6,
42934     
42935     store : false,
42936     
42937     getAutoCreate : function()
42938     {
42939         var align = this.labelAlign || this.parentLabelAlign();
42940         
42941         var id = Roo.id();
42942
42943         var cfg = {
42944             cls: 'form-group',
42945             cn: []
42946         };
42947
42948         var input =  {
42949             tag: 'input',
42950             id : id,
42951             cls : 'form-control roo-money-amount-input',
42952             autocomplete: 'new-password'
42953         };
42954         
42955         var hiddenInput = {
42956             tag: 'input',
42957             type: 'hidden',
42958             id: Roo.id(),
42959             cls: 'hidden-number-input'
42960         };
42961         
42962         if(this.max_length) {
42963             input.maxlength = this.max_length; 
42964         }
42965         
42966         if (this.name) {
42967             hiddenInput.name = this.name;
42968         }
42969
42970         if (this.disabled) {
42971             input.disabled = true;
42972         }
42973
42974         var clg = 12 - this.inputlg;
42975         var cmd = 12 - this.inputmd;
42976         var csm = 12 - this.inputsm;
42977         var cxs = 12 - this.inputxs;
42978         
42979         var container = {
42980             tag : 'div',
42981             cls : 'row roo-money-field',
42982             cn : [
42983                 {
42984                     tag : 'div',
42985                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
42986                     cn : [
42987                         {
42988                             tag : 'div',
42989                             cls: 'roo-select2-container input-group',
42990                             cn: [
42991                                 {
42992                                     tag : 'input',
42993                                     cls : 'form-control roo-money-currency-input',
42994                                     autocomplete: 'new-password',
42995                                     readOnly : 1,
42996                                     name : this.currencyName
42997                                 },
42998                                 {
42999                                     tag :'span',
43000                                     cls : 'input-group-addon',
43001                                     cn : [
43002                                         {
43003                                             tag: 'span',
43004                                             cls: 'caret'
43005                                         }
43006                                     ]
43007                                 }
43008                             ]
43009                         }
43010                     ]
43011                 },
43012                 {
43013                     tag : 'div',
43014                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43015                     cn : [
43016                         {
43017                             tag: 'div',
43018                             cls: this.hasFeedback ? 'has-feedback' : '',
43019                             cn: [
43020                                 input
43021                             ]
43022                         }
43023                     ]
43024                 }
43025             ]
43026             
43027         };
43028         
43029         if (this.fieldLabel.length) {
43030             var indicator = {
43031                 tag: 'i',
43032                 tooltip: 'This field is required'
43033             };
43034
43035             var label = {
43036                 tag: 'label',
43037                 'for':  id,
43038                 cls: 'control-label',
43039                 cn: []
43040             };
43041
43042             var label_text = {
43043                 tag: 'span',
43044                 html: this.fieldLabel
43045             };
43046
43047             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43048             label.cn = [
43049                 indicator,
43050                 label_text
43051             ];
43052
43053             if(this.indicatorpos == 'right') {
43054                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43055                 label.cn = [
43056                     label_text,
43057                     indicator
43058                 ];
43059             }
43060
43061             if(align == 'left') {
43062                 container = {
43063                     tag: 'div',
43064                     cn: [
43065                         container
43066                     ]
43067                 };
43068
43069                 if(this.labelWidth > 12){
43070                     label.style = "width: " + this.labelWidth + 'px';
43071                 }
43072                 if(this.labelWidth < 13 && this.labelmd == 0){
43073                     this.labelmd = this.labelWidth;
43074                 }
43075                 if(this.labellg > 0){
43076                     label.cls += ' col-lg-' + this.labellg;
43077                     input.cls += ' col-lg-' + (12 - this.labellg);
43078                 }
43079                 if(this.labelmd > 0){
43080                     label.cls += ' col-md-' + this.labelmd;
43081                     container.cls += ' col-md-' + (12 - this.labelmd);
43082                 }
43083                 if(this.labelsm > 0){
43084                     label.cls += ' col-sm-' + this.labelsm;
43085                     container.cls += ' col-sm-' + (12 - this.labelsm);
43086                 }
43087                 if(this.labelxs > 0){
43088                     label.cls += ' col-xs-' + this.labelxs;
43089                     container.cls += ' col-xs-' + (12 - this.labelxs);
43090                 }
43091             }
43092         }
43093
43094         cfg.cn = [
43095             label,
43096             container,
43097             hiddenInput
43098         ];
43099         
43100         var settings = this;
43101
43102         ['xs','sm','md','lg'].map(function(size){
43103             if (settings[size]) {
43104                 cfg.cls += ' col-' + size + '-' + settings[size];
43105             }
43106         });
43107         
43108         return cfg;
43109     },
43110     
43111     initEvents : function()
43112     {
43113         this.indicator = this.indicatorEl();
43114         
43115         this.initCurrencyEvent();
43116         
43117         this.initNumberEvent();
43118     },
43119     
43120     initCurrencyEvent : function()
43121     {
43122         if (!this.store) {
43123             throw "can not find store for combo";
43124         }
43125         
43126         this.store = Roo.factory(this.store, Roo.data);
43127         this.store.parent = this;
43128         
43129         this.createList();
43130         
43131         this.triggerEl = this.el.select('.input-group-addon', true).first();
43132         
43133         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43134         
43135         var _this = this;
43136         
43137         (function(){
43138             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43139             _this.list.setWidth(lw);
43140         }).defer(100);
43141         
43142         this.list.on('mouseover', this.onViewOver, this);
43143         this.list.on('mousemove', this.onViewMove, this);
43144         this.list.on('scroll', this.onViewScroll, this);
43145         
43146         if(!this.tpl){
43147             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43148         }
43149         
43150         this.view = new Roo.View(this.list, this.tpl, {
43151             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43152         });
43153         
43154         this.view.on('click', this.onViewClick, this);
43155         
43156         this.store.on('beforeload', this.onBeforeLoad, this);
43157         this.store.on('load', this.onLoad, this);
43158         this.store.on('loadexception', this.onLoadException, this);
43159         
43160         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43161             "up" : function(e){
43162                 this.inKeyMode = true;
43163                 this.selectPrev();
43164             },
43165
43166             "down" : function(e){
43167                 if(!this.isExpanded()){
43168                     this.onTriggerClick();
43169                 }else{
43170                     this.inKeyMode = true;
43171                     this.selectNext();
43172                 }
43173             },
43174
43175             "enter" : function(e){
43176                 this.collapse();
43177                 
43178                 if(this.fireEvent("specialkey", this, e)){
43179                     this.onViewClick(false);
43180                 }
43181                 
43182                 return true;
43183             },
43184
43185             "esc" : function(e){
43186                 this.collapse();
43187             },
43188
43189             "tab" : function(e){
43190                 this.collapse();
43191                 
43192                 if(this.fireEvent("specialkey", this, e)){
43193                     this.onViewClick(false);
43194                 }
43195                 
43196                 return true;
43197             },
43198
43199             scope : this,
43200
43201             doRelay : function(foo, bar, hname){
43202                 if(hname == 'down' || this.scope.isExpanded()){
43203                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43204                 }
43205                 return true;
43206             },
43207
43208             forceKeyDown: true
43209         });
43210         
43211         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43212         
43213     },
43214     
43215     initNumberEvent : function(e)
43216     {
43217         this.inputEl().on("keydown" , this.fireKey,  this);
43218         this.inputEl().on("focus", this.onFocus,  this);
43219         this.inputEl().on("blur", this.onBlur,  this);
43220         
43221         this.inputEl().relayEvent('keyup', this);
43222         
43223         if(this.indicator){
43224             this.indicator.addClass('invisible');
43225         }
43226  
43227         this.originalValue = this.getValue();
43228         
43229         if(this.validationEvent == 'keyup'){
43230             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43231             this.inputEl().on('keyup', this.filterValidation, this);
43232         }
43233         else if(this.validationEvent !== false){
43234             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43235         }
43236         
43237         if(this.selectOnFocus){
43238             this.on("focus", this.preFocus, this);
43239             
43240         }
43241         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43242             this.inputEl().on("keypress", this.filterKeys, this);
43243         } else {
43244             this.inputEl().relayEvent('keypress', this);
43245         }
43246         
43247         var allowed = "0123456789";
43248         
43249         if(this.allowDecimals){
43250             allowed += this.decimalSeparator;
43251         }
43252         
43253         if(this.allowNegative){
43254             allowed += "-";
43255         }
43256         
43257         if(this.thousandsDelimiter) {
43258             allowed += ",";
43259         }
43260         
43261         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43262         
43263         var keyPress = function(e){
43264             
43265             var k = e.getKey();
43266             
43267             var c = e.getCharCode();
43268             
43269             if(
43270                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43271                     allowed.indexOf(String.fromCharCode(c)) === -1
43272             ){
43273                 e.stopEvent();
43274                 return;
43275             }
43276             
43277             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43278                 return;
43279             }
43280             
43281             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43282                 e.stopEvent();
43283             }
43284         };
43285         
43286         this.inputEl().on("keypress", keyPress, this);
43287         
43288     },
43289     
43290     onTriggerClick : function(e)
43291     {   
43292         if(this.disabled){
43293             return;
43294         }
43295         
43296         this.page = 0;
43297         this.loadNext = false;
43298         
43299         if(this.isExpanded()){
43300             this.collapse();
43301             return;
43302         }
43303         
43304         this.hasFocus = true;
43305         
43306         if(this.triggerAction == 'all') {
43307             this.doQuery(this.allQuery, true);
43308             return;
43309         }
43310         
43311         this.doQuery(this.getRawValue());
43312     },
43313     
43314     getCurrency : function()
43315     {   
43316         var v = this.currencyEl().getValue();
43317         
43318         return v;
43319     },
43320     
43321     restrictHeight : function()
43322     {
43323         this.list.alignTo(this.currencyEl(), this.listAlign);
43324         this.list.alignTo(this.currencyEl(), this.listAlign);
43325     },
43326     
43327     onViewClick : function(view, doFocus, el, e)
43328     {
43329         var index = this.view.getSelectedIndexes()[0];
43330         
43331         var r = this.store.getAt(index);
43332         
43333         if(r){
43334             this.onSelect(r, index);
43335         }
43336     },
43337     
43338     onSelect : function(record, index){
43339         
43340         if(this.fireEvent('beforeselect', this, record, index) !== false){
43341         
43342             this.setFromCurrencyData(index > -1 ? record.data : false);
43343             
43344             this.collapse();
43345             
43346             this.fireEvent('select', this, record, index);
43347         }
43348     },
43349     
43350     setFromCurrencyData : function(o)
43351     {
43352         var currency = '';
43353         
43354         this.lastCurrency = o;
43355         
43356         if (this.currencyField) {
43357             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43358         } else {
43359             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43360         }
43361         
43362         this.lastSelectionText = currency;
43363         
43364         //setting default currency
43365         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43366             this.setCurrency(this.defaultCurrency);
43367             return;
43368         }
43369         
43370         this.setCurrency(currency);
43371     },
43372     
43373     setFromData : function(o)
43374     {
43375         var c = {};
43376         
43377         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43378         
43379         this.setFromCurrencyData(c);
43380         
43381         var value = '';
43382         
43383         if (this.name) {
43384             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43385         } else {
43386             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43387         }
43388         
43389         this.setValue(value);
43390         
43391     },
43392     
43393     setCurrency : function(v)
43394     {   
43395         this.currencyValue = v;
43396         
43397         if(this.rendered){
43398             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43399             this.validate();
43400         }
43401     },
43402     
43403     setValue : function(v)
43404     {
43405         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43406         
43407         this.value = v;
43408         
43409         if(this.rendered){
43410             
43411             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43412             
43413             this.inputEl().dom.value = (v == '') ? '' :
43414                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43415             
43416             if(!this.allowZero && v === '0') {
43417                 this.hiddenEl().dom.value = '';
43418                 this.inputEl().dom.value = '';
43419             }
43420             
43421             this.validate();
43422         }
43423     },
43424     
43425     getRawValue : function()
43426     {
43427         var v = this.inputEl().getValue();
43428         
43429         return v;
43430     },
43431     
43432     getValue : function()
43433     {
43434         return this.fixPrecision(this.parseValue(this.getRawValue()));
43435     },
43436     
43437     parseValue : function(value)
43438     {
43439         if(this.thousandsDelimiter) {
43440             value += "";
43441             r = new RegExp(",", "g");
43442             value = value.replace(r, "");
43443         }
43444         
43445         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43446         return isNaN(value) ? '' : value;
43447         
43448     },
43449     
43450     fixPrecision : function(value)
43451     {
43452         if(this.thousandsDelimiter) {
43453             value += "";
43454             r = new RegExp(",", "g");
43455             value = value.replace(r, "");
43456         }
43457         
43458         var nan = isNaN(value);
43459         
43460         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43461             return nan ? '' : value;
43462         }
43463         return parseFloat(value).toFixed(this.decimalPrecision);
43464     },
43465     
43466     decimalPrecisionFcn : function(v)
43467     {
43468         return Math.floor(v);
43469     },
43470     
43471     validateValue : function(value)
43472     {
43473         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43474             return false;
43475         }
43476         
43477         var num = this.parseValue(value);
43478         
43479         if(isNaN(num)){
43480             this.markInvalid(String.format(this.nanText, value));
43481             return false;
43482         }
43483         
43484         if(num < this.minValue){
43485             this.markInvalid(String.format(this.minText, this.minValue));
43486             return false;
43487         }
43488         
43489         if(num > this.maxValue){
43490             this.markInvalid(String.format(this.maxText, this.maxValue));
43491             return false;
43492         }
43493         
43494         return true;
43495     },
43496     
43497     validate : function()
43498     {
43499         if(this.disabled || this.allowBlank){
43500             this.markValid();
43501             return true;
43502         }
43503         
43504         var currency = this.getCurrency();
43505         
43506         if(this.validateValue(this.getRawValue()) && currency.length){
43507             this.markValid();
43508             return true;
43509         }
43510         
43511         this.markInvalid();
43512         return false;
43513     },
43514     
43515     getName: function()
43516     {
43517         return this.name;
43518     },
43519     
43520     beforeBlur : function()
43521     {
43522         if(!this.castInt){
43523             return;
43524         }
43525         
43526         var v = this.parseValue(this.getRawValue());
43527         
43528         if(v || v == 0){
43529             this.setValue(v);
43530         }
43531     },
43532     
43533     onBlur : function()
43534     {
43535         this.beforeBlur();
43536         
43537         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43538             //this.el.removeClass(this.focusClass);
43539         }
43540         
43541         this.hasFocus = false;
43542         
43543         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43544             this.validate();
43545         }
43546         
43547         var v = this.getValue();
43548         
43549         if(String(v) !== String(this.startValue)){
43550             this.fireEvent('change', this, v, this.startValue);
43551         }
43552         
43553         this.fireEvent("blur", this);
43554     },
43555     
43556     inputEl : function()
43557     {
43558         return this.el.select('.roo-money-amount-input', true).first();
43559     },
43560     
43561     currencyEl : function()
43562     {
43563         return this.el.select('.roo-money-currency-input', true).first();
43564     },
43565     
43566     hiddenEl : function()
43567     {
43568         return this.el.select('input.hidden-number-input',true).first();
43569     }
43570     
43571 });/**
43572  * @class Roo.bootstrap.BezierSignature
43573  * @extends Roo.bootstrap.Component
43574  * Bootstrap BezierSignature class
43575  * This script refer to:
43576  *    Title: Signature Pad
43577  *    Author: szimek
43578  *    Availability: https://github.com/szimek/signature_pad
43579  *
43580  * @constructor
43581  * Create a new BezierSignature
43582  * @param {Object} config The config object
43583  */
43584
43585 Roo.bootstrap.BezierSignature = function(config){
43586     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43587     this.addEvents({
43588         "resize" : true
43589     });
43590 };
43591
43592 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43593 {
43594      
43595     curve_data: [],
43596     
43597     is_empty: true,
43598     
43599     mouse_btn_down: true,
43600     
43601     /**
43602      * @cfg {int} canvas height
43603      */
43604     canvas_height: '200px',
43605     
43606     /**
43607      * @cfg {float|function} Radius of a single dot.
43608      */ 
43609     dot_size: false,
43610     
43611     /**
43612      * @cfg {float} Minimum width of a line. Defaults to 0.5.
43613      */
43614     min_width: 0.5,
43615     
43616     /**
43617      * @cfg {float} Maximum width of a line. Defaults to 2.5.
43618      */
43619     max_width: 2.5,
43620     
43621     /**
43622      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43623      */
43624     throttle: 16,
43625     
43626     /**
43627      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43628      */
43629     min_distance: 5,
43630     
43631     /**
43632      * @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.
43633      */
43634     bg_color: 'rgba(0, 0, 0, 0)',
43635     
43636     /**
43637      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43638      */
43639     dot_color: 'black',
43640     
43641     /**
43642      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43643      */ 
43644     velocity_filter_weight: 0.7,
43645     
43646     /**
43647      * @cfg {function} Callback when stroke begin. 
43648      */
43649     onBegin: false,
43650     
43651     /**
43652      * @cfg {function} Callback when stroke end.
43653      */
43654     onEnd: false,
43655     
43656     getAutoCreate : function()
43657     {
43658         var cls = 'roo-signature column';
43659         
43660         if(this.cls){
43661             cls += ' ' + this.cls;
43662         }
43663         
43664         var col_sizes = [
43665             'lg',
43666             'md',
43667             'sm',
43668             'xs'
43669         ];
43670         
43671         for(var i = 0; i < col_sizes.length; i++) {
43672             if(this[col_sizes[i]]) {
43673                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43674             }
43675         }
43676         
43677         var cfg = {
43678             tag: 'div',
43679             cls: cls,
43680             cn: [
43681                 {
43682                     tag: 'div',
43683                     cls: 'roo-signature-body',
43684                     cn: [
43685                         {
43686                             tag: 'canvas',
43687                             cls: 'roo-signature-body-canvas',
43688                             height: this.canvas_height,
43689                             width: this.canvas_width
43690                         }
43691                     ]
43692                 },
43693                 {
43694                     tag: 'input',
43695                     type: 'file',
43696                     style: 'display: none'
43697                 }
43698             ]
43699         };
43700         
43701         return cfg;
43702     },
43703     
43704     initEvents: function() 
43705     {
43706         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43707         
43708         var canvas = this.canvasEl();
43709         
43710         // mouse && touch event swapping...
43711         canvas.dom.style.touchAction = 'none';
43712         canvas.dom.style.msTouchAction = 'none';
43713         
43714         this.mouse_btn_down = false;
43715         canvas.on('mousedown', this._handleMouseDown, this);
43716         canvas.on('mousemove', this._handleMouseMove, this);
43717         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43718         
43719         if (window.PointerEvent) {
43720             canvas.on('pointerdown', this._handleMouseDown, this);
43721             canvas.on('pointermove', this._handleMouseMove, this);
43722             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43723         }
43724         
43725         if ('ontouchstart' in window) {
43726             canvas.on('touchstart', this._handleTouchStart, this);
43727             canvas.on('touchmove', this._handleTouchMove, this);
43728             canvas.on('touchend', this._handleTouchEnd, this);
43729         }
43730         
43731         Roo.EventManager.onWindowResize(this.resize, this, true);
43732         
43733         // file input event
43734         this.fileEl().on('change', this.uploadImage, this);
43735         
43736         this.clear();
43737         
43738         this.resize();
43739     },
43740     
43741     resize: function(){
43742         
43743         var canvas = this.canvasEl().dom;
43744         var ctx = this.canvasElCtx();
43745         var img_data = false;
43746         
43747         if(canvas.width > 0) {
43748             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
43749         }
43750         // setting canvas width will clean img data
43751         canvas.width = 0;
43752         
43753         var style = window.getComputedStyle ? 
43754             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
43755             
43756         var padding_left = parseInt(style.paddingLeft) || 0;
43757         var padding_right = parseInt(style.paddingRight) || 0;
43758         
43759         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
43760         
43761         if(img_data) {
43762             ctx.putImageData(img_data, 0, 0);
43763         }
43764     },
43765     
43766     _handleMouseDown: function(e)
43767     {
43768         if (e.browserEvent.which === 1) {
43769             this.mouse_btn_down = true;
43770             this.strokeBegin(e);
43771         }
43772     },
43773     
43774     _handleMouseMove: function (e)
43775     {
43776         if (this.mouse_btn_down) {
43777             this.strokeMoveUpdate(e);
43778         }
43779     },
43780     
43781     _handleMouseUp: function (e)
43782     {
43783         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
43784             this.mouse_btn_down = false;
43785             this.strokeEnd(e);
43786         }
43787     },
43788     
43789     _handleTouchStart: function (e) {
43790         
43791         e.preventDefault();
43792         if (e.browserEvent.targetTouches.length === 1) {
43793             // var touch = e.browserEvent.changedTouches[0];
43794             // this.strokeBegin(touch);
43795             
43796              this.strokeBegin(e); // assume e catching the correct xy...
43797         }
43798     },
43799     
43800     _handleTouchMove: function (e) {
43801         e.preventDefault();
43802         // var touch = event.targetTouches[0];
43803         // _this._strokeMoveUpdate(touch);
43804         this.strokeMoveUpdate(e);
43805     },
43806     
43807     _handleTouchEnd: function (e) {
43808         var wasCanvasTouched = e.target === this.canvasEl().dom;
43809         if (wasCanvasTouched) {
43810             e.preventDefault();
43811             // var touch = event.changedTouches[0];
43812             // _this._strokeEnd(touch);
43813             this.strokeEnd(e);
43814         }
43815     },
43816     
43817     reset: function () {
43818         this._lastPoints = [];
43819         this._lastVelocity = 0;
43820         this._lastWidth = (this.min_width + this.max_width) / 2;
43821         this.canvasElCtx().fillStyle = this.dot_color;
43822     },
43823     
43824     strokeMoveUpdate: function(e)
43825     {
43826         this.strokeUpdate(e);
43827         
43828         if (this.throttle) {
43829             this.throttleStroke(this.strokeUpdate, this.throttle);
43830         }
43831         else {
43832             this.strokeUpdate(e);
43833         }
43834     },
43835     
43836     strokeBegin: function(e)
43837     {
43838         var newPointGroup = {
43839             color: this.dot_color,
43840             points: []
43841         };
43842         
43843         if (typeof this.onBegin === 'function') {
43844             this.onBegin(e);
43845         }
43846         
43847         this.curve_data.push(newPointGroup);
43848         this.reset();
43849         this.strokeUpdate(e);
43850     },
43851     
43852     strokeUpdate: function(e)
43853     {
43854         var rect = this.canvasEl().dom.getBoundingClientRect();
43855         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
43856         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
43857         var lastPoints = lastPointGroup.points;
43858         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
43859         var isLastPointTooClose = lastPoint
43860             ? point.distanceTo(lastPoint) <= this.min_distance
43861             : false;
43862         var color = lastPointGroup.color;
43863         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
43864             var curve = this.addPoint(point);
43865             if (!lastPoint) {
43866                 this.drawDot({color: color, point: point});
43867             }
43868             else if (curve) {
43869                 this.drawCurve({color: color, curve: curve});
43870             }
43871             lastPoints.push({
43872                 time: point.time,
43873                 x: point.x,
43874                 y: point.y
43875             });
43876         }
43877     },
43878     
43879     strokeEnd: function(e)
43880     {
43881         this.strokeUpdate(e);
43882         if (typeof this.onEnd === 'function') {
43883             this.onEnd(e);
43884         }
43885     },
43886     
43887     addPoint:  function (point) {
43888         var _lastPoints = this._lastPoints;
43889         _lastPoints.push(point);
43890         if (_lastPoints.length > 2) {
43891             if (_lastPoints.length === 3) {
43892                 _lastPoints.unshift(_lastPoints[0]);
43893             }
43894             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
43895             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
43896             _lastPoints.shift();
43897             return curve;
43898         }
43899         return null;
43900     },
43901     
43902     calculateCurveWidths: function (startPoint, endPoint) {
43903         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
43904             (1 - this.velocity_filter_weight) * this._lastVelocity;
43905
43906         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
43907         var widths = {
43908             end: newWidth,
43909             start: this._lastWidth
43910         };
43911         
43912         this._lastVelocity = velocity;
43913         this._lastWidth = newWidth;
43914         return widths;
43915     },
43916     
43917     drawDot: function (_a) {
43918         var color = _a.color, point = _a.point;
43919         var ctx = this.canvasElCtx();
43920         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
43921         ctx.beginPath();
43922         this.drawCurveSegment(point.x, point.y, width);
43923         ctx.closePath();
43924         ctx.fillStyle = color;
43925         ctx.fill();
43926     },
43927     
43928     drawCurve: function (_a) {
43929         var color = _a.color, curve = _a.curve;
43930         var ctx = this.canvasElCtx();
43931         var widthDelta = curve.endWidth - curve.startWidth;
43932         var drawSteps = Math.floor(curve.length()) * 2;
43933         ctx.beginPath();
43934         ctx.fillStyle = color;
43935         for (var i = 0; i < drawSteps; i += 1) {
43936         var t = i / drawSteps;
43937         var tt = t * t;
43938         var ttt = tt * t;
43939         var u = 1 - t;
43940         var uu = u * u;
43941         var uuu = uu * u;
43942         var x = uuu * curve.startPoint.x;
43943         x += 3 * uu * t * curve.control1.x;
43944         x += 3 * u * tt * curve.control2.x;
43945         x += ttt * curve.endPoint.x;
43946         var y = uuu * curve.startPoint.y;
43947         y += 3 * uu * t * curve.control1.y;
43948         y += 3 * u * tt * curve.control2.y;
43949         y += ttt * curve.endPoint.y;
43950         var width = curve.startWidth + ttt * widthDelta;
43951         this.drawCurveSegment(x, y, width);
43952         }
43953         ctx.closePath();
43954         ctx.fill();
43955     },
43956     
43957     drawCurveSegment: function (x, y, width) {
43958         var ctx = this.canvasElCtx();
43959         ctx.moveTo(x, y);
43960         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
43961         this.is_empty = false;
43962     },
43963     
43964     clear: function()
43965     {
43966         var ctx = this.canvasElCtx();
43967         var canvas = this.canvasEl().dom;
43968         ctx.fillStyle = this.bg_color;
43969         ctx.clearRect(0, 0, canvas.width, canvas.height);
43970         ctx.fillRect(0, 0, canvas.width, canvas.height);
43971         this.curve_data = [];
43972         this.reset();
43973         this.is_empty = true;
43974     },
43975     
43976     fileEl: function()
43977     {
43978         return  this.el.select('input',true).first();
43979     },
43980     
43981     canvasEl: function()
43982     {
43983         return this.el.select('canvas',true).first();
43984     },
43985     
43986     canvasElCtx: function()
43987     {
43988         return this.el.select('canvas',true).first().dom.getContext('2d');
43989     },
43990     
43991     getImage: function(type)
43992     {
43993         if(this.is_empty) {
43994             return false;
43995         }
43996         
43997         // encryption ?
43998         return this.canvasEl().dom.toDataURL('image/'+type, 1);
43999     },
44000     
44001     drawFromImage: function(img_src)
44002     {
44003         var img = new Image();
44004         
44005         img.onload = function(){
44006             this.canvasElCtx().drawImage(img, 0, 0);
44007         }.bind(this);
44008         
44009         img.src = img_src;
44010         
44011         this.is_empty = false;
44012     },
44013     
44014     selectImage: function()
44015     {
44016         this.fileEl().dom.click();
44017     },
44018     
44019     uploadImage: function(e)
44020     {
44021         var reader = new FileReader();
44022         
44023         reader.onload = function(e){
44024             var img = new Image();
44025             img.onload = function(){
44026                 this.reset();
44027                 this.canvasElCtx().drawImage(img, 0, 0);
44028             }.bind(this);
44029             img.src = e.target.result;
44030         }.bind(this);
44031         
44032         reader.readAsDataURL(e.target.files[0]);
44033     },
44034     
44035     // Bezier Point Constructor
44036     Point: (function () {
44037         function Point(x, y, time) {
44038             this.x = x;
44039             this.y = y;
44040             this.time = time || Date.now();
44041         }
44042         Point.prototype.distanceTo = function (start) {
44043             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44044         };
44045         Point.prototype.equals = function (other) {
44046             return this.x === other.x && this.y === other.y && this.time === other.time;
44047         };
44048         Point.prototype.velocityFrom = function (start) {
44049             return this.time !== start.time
44050             ? this.distanceTo(start) / (this.time - start.time)
44051             : 0;
44052         };
44053         return Point;
44054     }()),
44055     
44056     
44057     // Bezier Constructor
44058     Bezier: (function () {
44059         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44060             this.startPoint = startPoint;
44061             this.control2 = control2;
44062             this.control1 = control1;
44063             this.endPoint = endPoint;
44064             this.startWidth = startWidth;
44065             this.endWidth = endWidth;
44066         }
44067         Bezier.fromPoints = function (points, widths, scope) {
44068             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44069             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44070             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44071         };
44072         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44073             var dx1 = s1.x - s2.x;
44074             var dy1 = s1.y - s2.y;
44075             var dx2 = s2.x - s3.x;
44076             var dy2 = s2.y - s3.y;
44077             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44078             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44079             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44080             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44081             var dxm = m1.x - m2.x;
44082             var dym = m1.y - m2.y;
44083             var k = l2 / (l1 + l2);
44084             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44085             var tx = s2.x - cm.x;
44086             var ty = s2.y - cm.y;
44087             return {
44088                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44089                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44090             };
44091         };
44092         Bezier.prototype.length = function () {
44093             var steps = 10;
44094             var length = 0;
44095             var px;
44096             var py;
44097             for (var i = 0; i <= steps; i += 1) {
44098                 var t = i / steps;
44099                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44100                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44101                 if (i > 0) {
44102                     var xdiff = cx - px;
44103                     var ydiff = cy - py;
44104                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44105                 }
44106                 px = cx;
44107                 py = cy;
44108             }
44109             return length;
44110         };
44111         Bezier.prototype.point = function (t, start, c1, c2, end) {
44112             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44113             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44114             + (3.0 * c2 * (1.0 - t) * t * t)
44115             + (end * t * t * t);
44116         };
44117         return Bezier;
44118     }()),
44119     
44120     throttleStroke: function(fn, wait) {
44121       if (wait === void 0) { wait = 250; }
44122       var previous = 0;
44123       var timeout = null;
44124       var result;
44125       var storedContext;
44126       var storedArgs;
44127       var later = function () {
44128           previous = Date.now();
44129           timeout = null;
44130           result = fn.apply(storedContext, storedArgs);
44131           if (!timeout) {
44132               storedContext = null;
44133               storedArgs = [];
44134           }
44135       };
44136       return function wrapper() {
44137           var args = [];
44138           for (var _i = 0; _i < arguments.length; _i++) {
44139               args[_i] = arguments[_i];
44140           }
44141           var now = Date.now();
44142           var remaining = wait - (now - previous);
44143           storedContext = this;
44144           storedArgs = args;
44145           if (remaining <= 0 || remaining > wait) {
44146               if (timeout) {
44147                   clearTimeout(timeout);
44148                   timeout = null;
44149               }
44150               previous = now;
44151               result = fn.apply(storedContext, storedArgs);
44152               if (!timeout) {
44153                   storedContext = null;
44154                   storedArgs = [];
44155               }
44156           }
44157           else if (!timeout) {
44158               timeout = window.setTimeout(later, remaining);
44159           }
44160           return result;
44161       };
44162   }
44163   
44164 });
44165
44166  
44167
44168