roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); /*
18  * Based on:
19  * Ext JS Library 1.1.1
20  * Copyright(c) 2006-2007, Ext JS, LLC.
21  *
22  * Originally Released Under LGPL - original licence link has changed is not relivant.
23  *
24  * Fork - LGPL
25  * <script type="text/javascript">
26  */
27
28
29 /**
30  * @class Roo.Shadow
31  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
32  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
33  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
34  * @constructor
35  * Create a new Shadow
36  * @param {Object} config The config object
37  */
38 Roo.Shadow = function(config){
39     Roo.apply(this, config);
40     if(typeof this.mode != "string"){
41         this.mode = this.defaultMode;
42     }
43     var o = this.offset, a = {h: 0};
44     var rad = Math.floor(this.offset/2);
45     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
46         case "drop":
47             a.w = 0;
48             a.l = a.t = o;
49             a.t -= 1;
50             if(Roo.isIE){
51                 a.l -= this.offset + rad;
52                 a.t -= this.offset + rad;
53                 a.w -= rad;
54                 a.h -= rad;
55                 a.t += 1;
56             }
57         break;
58         case "sides":
59             a.w = (o*2);
60             a.l = -o;
61             a.t = o-1;
62             if(Roo.isIE){
63                 a.l -= (this.offset - rad);
64                 a.t -= this.offset + rad;
65                 a.l += 1;
66                 a.w -= (this.offset - rad)*2;
67                 a.w -= rad + 1;
68                 a.h -= 1;
69             }
70         break;
71         case "frame":
72             a.w = a.h = (o*2);
73             a.l = a.t = -o;
74             a.t += 1;
75             a.h -= 2;
76             if(Roo.isIE){
77                 a.l -= (this.offset - rad);
78                 a.t -= (this.offset - rad);
79                 a.l += 1;
80                 a.w -= (this.offset + rad + 1);
81                 a.h -= (this.offset + rad);
82                 a.h += 1;
83             }
84         break;
85     };
86
87     this.adjusts = a;
88 };
89
90 Roo.Shadow.prototype = {
91     /**
92      * @cfg {String} mode
93      * The shadow display mode.  Supports the following options:<br />
94      * sides: Shadow displays on both sides and bottom only<br />
95      * frame: Shadow displays equally on all four sides<br />
96      * drop: Traditional bottom-right drop shadow (default)
97      */
98     /**
99      * @cfg {String} offset
100      * The number of pixels to offset the shadow from the element (defaults to 4)
101      */
102     offset: 4,
103
104     // private
105     defaultMode: "drop",
106
107     /**
108      * Displays the shadow under the target element
109      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
110      */
111     show : function(target){
112         target = Roo.get(target);
113         if(!this.el){
114             this.el = Roo.Shadow.Pool.pull();
115             if(this.el.dom.nextSibling != target.dom){
116                 this.el.insertBefore(target);
117             }
118         }
119         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
120         if(Roo.isIE){
121             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
122         }
123         this.realign(
124             target.getLeft(true),
125             target.getTop(true),
126             target.getWidth(),
127             target.getHeight()
128         );
129         this.el.dom.style.display = "block";
130     },
131
132     /**
133      * Returns true if the shadow is visible, else false
134      */
135     isVisible : function(){
136         return this.el ? true : false;  
137     },
138
139     /**
140      * Direct alignment when values are already available. Show must be called at least once before
141      * calling this method to ensure it is initialized.
142      * @param {Number} left The target element left position
143      * @param {Number} top The target element top position
144      * @param {Number} width The target element width
145      * @param {Number} height The target element height
146      */
147     realign : function(l, t, w, h){
148         if(!this.el){
149             return;
150         }
151         var a = this.adjusts, d = this.el.dom, s = d.style;
152         var iea = 0;
153         s.left = (l+a.l)+"px";
154         s.top = (t+a.t)+"px";
155         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
156  
157         if(s.width != sws || s.height != shs){
158             s.width = sws;
159             s.height = shs;
160             if(!Roo.isIE){
161                 var cn = d.childNodes;
162                 var sww = Math.max(0, (sw-12))+"px";
163                 cn[0].childNodes[1].style.width = sww;
164                 cn[1].childNodes[1].style.width = sww;
165                 cn[2].childNodes[1].style.width = sww;
166                 cn[1].style.height = Math.max(0, (sh-12))+"px";
167             }
168         }
169     },
170
171     /**
172      * Hides this shadow
173      */
174     hide : function(){
175         if(this.el){
176             this.el.dom.style.display = "none";
177             Roo.Shadow.Pool.push(this.el);
178             delete this.el;
179         }
180     },
181
182     /**
183      * Adjust the z-index of this shadow
184      * @param {Number} zindex The new z-index
185      */
186     setZIndex : function(z){
187         this.zIndex = z;
188         if(this.el){
189             this.el.setStyle("z-index", z);
190         }
191     }
192 };
193
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
196     var p = [];
197     var markup = Roo.isIE ?
198                  '<div class="x-ie-shadow"></div>' :
199                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
200     return {
201         pull : function(){
202             var sh = p.shift();
203             if(!sh){
204                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205                 sh.autoBoxAdjust = false;
206             }
207             return sh;
208         },
209
210         push : function(sh){
211             p.push(sh);
212         }
213     };
214 }();/*
215  * - LGPL
216  *
217  * base class for bootstrap elements.
218  * 
219  */
220
221 Roo.bootstrap = Roo.bootstrap || {};
222 /**
223  * @class Roo.bootstrap.Component
224  * @extends Roo.Component
225  * Bootstrap Component base class
226  * @cfg {String} cls css class
227  * @cfg {String} style any extra css
228  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
230  * @cfg {string} dataId cutomer id
231  * @cfg {string} name Specifies name attribute
232  * @cfg {string} tooltip  Text for the tooltip
233  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
234  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
235  
236  * @constructor
237  * Do not use directly - it does not do anything..
238  * @param {Object} config The config object
239  */
240
241
242
243 Roo.bootstrap.Component = function(config){
244     Roo.bootstrap.Component.superclass.constructor.call(this, config);
245        
246     this.addEvents({
247         /**
248          * @event childrenrendered
249          * Fires when the children have been rendered..
250          * @param {Roo.bootstrap.Component} this
251          */
252         "childrenrendered" : true
253         
254         
255         
256     });
257     
258     
259 };
260
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
262     
263     
264     allowDomMove : false, // to stop relocations in parent onRender...
265     
266     cls : false,
267     
268     style : false,
269     
270     autoCreate : false,
271     
272     tooltip : null,
273     /**
274      * Initialize Events for the element
275      */
276     initEvents : function() { },
277     
278     xattr : false,
279     
280     parentId : false,
281     
282     can_build_overlaid : true,
283     
284     container_method : false,
285     
286     dataId : false,
287     
288     name : false,
289     
290     parent: function() {
291         // returns the parent component..
292         return Roo.ComponentMgr.get(this.parentId)
293         
294         
295     },
296     
297     // private
298     onRender : function(ct, position)
299     {
300        // Roo.log("Call onRender: " + this.xtype);
301         
302         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
303         
304         if(this.el){
305             if (this.el.attr('xtype')) {
306                 this.el.attr('xtypex', this.el.attr('xtype'));
307                 this.el.dom.removeAttribute('xtype');
308                 
309                 this.initEvents();
310             }
311             
312             return;
313         }
314         
315          
316         
317         var cfg = Roo.apply({},  this.getAutoCreate());
318         
319         cfg.id = this.id || Roo.id();
320         
321         // fill in the extra attributes 
322         if (this.xattr && typeof(this.xattr) =='object') {
323             for (var i in this.xattr) {
324                 cfg[i] = this.xattr[i];
325             }
326         }
327         
328         if(this.dataId){
329             cfg.dataId = this.dataId;
330         }
331         
332         if (this.cls) {
333             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
334         }
335         
336         if (this.style) { // fixme needs to support more complex style data.
337             cfg.style = this.style;
338         }
339         
340         if(this.name){
341             cfg.name = this.name;
342         }
343         
344         this.el = ct.createChild(cfg, position);
345         
346         if (this.tooltip) {
347             this.tooltipEl().attr('tooltip', this.tooltip);
348         }
349         
350         if(this.tabIndex !== undefined){
351             this.el.dom.setAttribute('tabIndex', this.tabIndex);
352         }
353         
354         this.initEvents();
355         
356     },
357     /**
358      * Fetch the element to add children to
359      * @return {Roo.Element} defaults to this.el
360      */
361     getChildContainer : function()
362     {
363         return this.el;
364     },
365     /**
366      * Fetch the element to display the tooltip on.
367      * @return {Roo.Element} defaults to this.el
368      */
369     tooltipEl : function()
370     {
371         return this.el;
372     },
373         
374     addxtype  : function(tree,cntr)
375     {
376         var cn = this;
377         
378         cn = Roo.factory(tree);
379         //Roo.log(['addxtype', cn]);
380            
381         cn.parentType = this.xtype; //??
382         cn.parentId = this.id;
383         
384         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
385         if (typeof(cn.container_method) == 'string') {
386             cntr = cn.container_method;
387         }
388         
389         
390         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
391         
392         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
393         
394         var build_from_html =  Roo.XComponent.build_from_html;
395           
396         var is_body  = (tree.xtype == 'Body') ;
397           
398         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
399           
400         var self_cntr_el = Roo.get(this[cntr](false));
401         
402         // do not try and build conditional elements 
403         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
404             return false;
405         }
406         
407         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
408             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
409                 return this.addxtypeChild(tree,cntr, is_body);
410             }
411             
412             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
413                 
414             if(echild){
415                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
416             }
417             
418             Roo.log('skipping render');
419             return cn;
420             
421         }
422         
423         var ret = false;
424         if (!build_from_html) {
425             return false;
426         }
427         
428         // this i think handles overlaying multiple children of the same type
429         // with the sam eelement.. - which might be buggy..
430         while (true) {
431             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
432             
433             if (!echild) {
434                 break;
435             }
436             
437             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
438                 break;
439             }
440             
441             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
442         }
443        
444         return ret;
445     },
446     
447     
448     addxtypeChild : function (tree, cntr, is_body)
449     {
450         Roo.debug && Roo.log('addxtypeChild:' + cntr);
451         var cn = this;
452         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
453         
454         
455         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
456                     (typeof(tree['flexy:foreach']) != 'undefined');
457           
458     
459         
460         skip_children = false;
461         // render the element if it's not BODY.
462         if (!is_body) {
463             
464             // if parent was disabled, then do not try and create the children..
465             if(!this[cntr](true)){
466                 tree.items = [];
467                 return tree;
468             }
469            
470             cn = Roo.factory(tree);
471            
472             cn.parentType = this.xtype; //??
473             cn.parentId = this.id;
474             
475             var build_from_html =  Roo.XComponent.build_from_html;
476             
477             
478             // does the container contain child eleemnts with 'xtype' attributes.
479             // that match this xtype..
480             // note - when we render we create these as well..
481             // so we should check to see if body has xtype set.
482             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
483                
484                 var self_cntr_el = Roo.get(this[cntr](false));
485                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
486                 if (echild) { 
487                     //Roo.log(Roo.XComponent.build_from_html);
488                     //Roo.log("got echild:");
489                     //Roo.log(echild);
490                 }
491                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
492                 // and are not displayed -this causes this to use up the wrong element when matching.
493                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
494                 
495                 
496                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
497                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
498                   
499                   
500                   
501                     cn.el = echild;
502                   //  Roo.log("GOT");
503                     //echild.dom.removeAttribute('xtype');
504                 } else {
505                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
506                     Roo.debug && Roo.log(self_cntr_el);
507                     Roo.debug && Roo.log(echild);
508                     Roo.debug && Roo.log(cn);
509                 }
510             }
511            
512             
513            
514             // if object has flexy:if - then it may or may not be rendered.
515             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
516                 // skip a flexy if element.
517                 Roo.debug && Roo.log('skipping render');
518                 Roo.debug && Roo.log(tree);
519                 if (!cn.el) {
520                     Roo.debug && Roo.log('skipping all children');
521                     skip_children = true;
522                 }
523                 
524              } else {
525                  
526                 // actually if flexy:foreach is found, we really want to create 
527                 // multiple copies here...
528                 //Roo.log('render');
529                 //Roo.log(this[cntr]());
530                 // some elements do not have render methods.. like the layouts...
531                 /*
532                 if(this[cntr](true) === false){
533                     cn.items = [];
534                     return cn;
535                 }
536                 */
537                 cn.render && cn.render(this[cntr](true));
538                 
539              }
540             // then add the element..
541         }
542          
543         // handle the kids..
544         
545         var nitems = [];
546         /*
547         if (typeof (tree.menu) != 'undefined') {
548             tree.menu.parentType = cn.xtype;
549             tree.menu.triggerEl = cn.el;
550             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
551             
552         }
553         */
554         if (!tree.items || !tree.items.length) {
555             cn.items = nitems;
556             //Roo.log(["no children", this]);
557             
558             return cn;
559         }
560          
561         var items = tree.items;
562         delete tree.items;
563         
564         //Roo.log(items.length);
565             // add the items..
566         if (!skip_children) {    
567             for(var i =0;i < items.length;i++) {
568               //  Roo.log(['add child', items[i]]);
569                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
570             }
571         }
572         
573         cn.items = nitems;
574         
575         //Roo.log("fire childrenrendered");
576         
577         cn.fireEvent('childrenrendered', this);
578         
579         return cn;
580     },
581     
582     /**
583      * Set the element that will be used to show or hide
584      */
585     setVisibilityEl : function(el)
586     {
587         this.visibilityEl = el;
588     },
589     
590      /**
591      * Get the element that will be used to show or hide
592      */
593     getVisibilityEl : function()
594     {
595         if (typeof(this.visibilityEl) == 'object') {
596             return this.visibilityEl;
597         }
598         
599         if (typeof(this.visibilityEl) == 'string') {
600             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
601         }
602         
603         return this.getEl();
604     },
605     
606     /**
607      * Show a component - removes 'hidden' class
608      */
609     show : function()
610     {
611         if(!this.getVisibilityEl()){
612             return;
613         }
614          
615         this.getVisibilityEl().removeClass(['hidden','d-none']);
616         
617         this.fireEvent('show', this);
618         
619         
620     },
621     /**
622      * Hide a component - adds 'hidden' class
623      */
624     hide: function()
625     {
626         if(!this.getVisibilityEl()){
627             return;
628         }
629         
630         this.getVisibilityEl().addClass(['hidden','d-none']);
631         
632         this.fireEvent('hide', this);
633         
634     }
635 });
636
637  /*
638  * - LGPL
639  *
640  * element
641  * 
642  */
643
644 /**
645  * @class Roo.bootstrap.Element
646  * @extends Roo.bootstrap.Component
647  * Bootstrap Element class
648  * @cfg {String} html contents of the element
649  * @cfg {String} tag tag of the element
650  * @cfg {String} cls class of the element
651  * @cfg {Boolean} preventDefault (true|false) default false
652  * @cfg {Boolean} clickable (true|false) default false
653  * 
654  * @constructor
655  * Create a new Element
656  * @param {Object} config The config object
657  */
658
659 Roo.bootstrap.Element = function(config){
660     Roo.bootstrap.Element.superclass.constructor.call(this, config);
661     
662     this.addEvents({
663         // raw events
664         /**
665          * @event click
666          * When a element is chick
667          * @param {Roo.bootstrap.Element} this
668          * @param {Roo.EventObject} e
669          */
670         "click" : true
671     });
672 };
673
674 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
675     
676     tag: 'div',
677     cls: '',
678     html: '',
679     preventDefault: false, 
680     clickable: false,
681     
682     getAutoCreate : function(){
683         
684         var cfg = {
685             tag: this.tag,
686             // cls: this.cls, double assign in parent class Component.js :: onRender
687             html: this.html
688         };
689         
690         return cfg;
691     },
692     
693     initEvents: function() 
694     {
695         Roo.bootstrap.Element.superclass.initEvents.call(this);
696         
697         if(this.clickable){
698             this.el.on('click', this.onClick, this);
699         }
700         
701     },
702     
703     onClick : function(e)
704     {
705         if(this.preventDefault){
706             e.preventDefault();
707         }
708         
709         this.fireEvent('click', this, e);
710     },
711     
712     getValue : function()
713     {
714         return this.el.dom.innerHTML;
715     },
716     
717     setValue : function(value)
718     {
719         this.el.dom.innerHTML = value;
720     }
721    
722 });
723
724  
725
726  /*
727  * - LGPL
728  *
729  * dropable area
730  * 
731  */
732
733 /**
734  * @class Roo.bootstrap.DropTarget
735  * @extends Roo.bootstrap.Element
736  * Bootstrap DropTarget class
737  
738  * @cfg {string} name dropable name
739  * 
740  * @constructor
741  * Create a new Dropable Area
742  * @param {Object} config The config object
743  */
744
745 Roo.bootstrap.DropTarget = function(config){
746     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
747     
748     this.addEvents({
749         // raw events
750         /**
751          * @event click
752          * When a element is chick
753          * @param {Roo.bootstrap.Element} this
754          * @param {Roo.EventObject} e
755          */
756         "drop" : true
757     });
758 };
759
760 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
761     
762     
763     getAutoCreate : function(){
764         
765          
766     },
767     
768     initEvents: function() 
769     {
770         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
771         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
772             ddGroup: this.name,
773             listeners : {
774                 drop : this.dragDrop.createDelegate(this),
775                 enter : this.dragEnter.createDelegate(this),
776                 out : this.dragOut.createDelegate(this),
777                 over : this.dragOver.createDelegate(this)
778             }
779             
780         });
781         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
782     },
783     
784     dragDrop : function(source,e,data)
785     {
786         // user has to decide how to impliment this.
787         Roo.log('drop');
788         Roo.log(this);
789         //this.fireEvent('drop', this, source, e ,data);
790         return false;
791     },
792     
793     dragEnter : function(n, dd, e, data)
794     {
795         // probably want to resize the element to match the dropped element..
796         Roo.log("enter");
797         this.originalSize = this.el.getSize();
798         this.el.setSize( n.el.getSize());
799         this.dropZone.DDM.refreshCache(this.name);
800         Roo.log([n, dd, e, data]);
801     },
802     
803     dragOut : function(value)
804     {
805         // resize back to normal
806         Roo.log("out");
807         this.el.setSize(this.originalSize);
808         this.dropZone.resetConstraints();
809     },
810     
811     dragOver : function()
812     {
813         // ??? do nothing?
814     }
815    
816 });
817
818  
819
820  /*
821  * - LGPL
822  *
823  * Body
824  *
825  */
826
827 /**
828  * @class Roo.bootstrap.Body
829  * @extends Roo.bootstrap.Component
830  * Bootstrap Body class
831  *
832  * @constructor
833  * Create a new body
834  * @param {Object} config The config object
835  */
836
837 Roo.bootstrap.Body = function(config){
838
839     config = config || {};
840
841     Roo.bootstrap.Body.superclass.constructor.call(this, config);
842     this.el = Roo.get(config.el ? config.el : document.body );
843     if (this.cls && this.cls.length) {
844         Roo.get(document.body).addClass(this.cls);
845     }
846 };
847
848 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
849
850     is_body : true,// just to make sure it's constructed?
851
852         autoCreate : {
853         cls: 'container'
854     },
855     onRender : function(ct, position)
856     {
857        /* Roo.log("Roo.bootstrap.Body - onRender");
858         if (this.cls && this.cls.length) {
859             Roo.get(document.body).addClass(this.cls);
860         }
861         // style??? xttr???
862         */
863     }
864
865
866
867
868 });
869 /*
870  * - LGPL
871  *
872  * button group
873  * 
874  */
875
876
877 /**
878  * @class Roo.bootstrap.ButtonGroup
879  * @extends Roo.bootstrap.Component
880  * Bootstrap ButtonGroup class
881  * @cfg {String} size lg | sm | xs (default empty normal)
882  * @cfg {String} align vertical | justified  (default none)
883  * @cfg {String} direction up | down (default down)
884  * @cfg {Boolean} toolbar false | true
885  * @cfg {Boolean} btn true | false
886  * 
887  * 
888  * @constructor
889  * Create a new Input
890  * @param {Object} config The config object
891  */
892
893 Roo.bootstrap.ButtonGroup = function(config){
894     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
895 };
896
897 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
898     
899     size: '',
900     align: '',
901     direction: '',
902     toolbar: false,
903     btn: true,
904
905     getAutoCreate : function(){
906         var cfg = {
907             cls: 'btn-group',
908             html : null
909         };
910         
911         cfg.html = this.html || cfg.html;
912         
913         if (this.toolbar) {
914             cfg = {
915                 cls: 'btn-toolbar',
916                 html: null
917             };
918             
919             return cfg;
920         }
921         
922         if (['vertical','justified'].indexOf(this.align)!==-1) {
923             cfg.cls = 'btn-group-' + this.align;
924             
925             if (this.align == 'justified') {
926                 console.log(this.items);
927             }
928         }
929         
930         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
931             cfg.cls += ' btn-group-' + this.size;
932         }
933         
934         if (this.direction == 'up') {
935             cfg.cls += ' dropup' ;
936         }
937         
938         return cfg;
939     },
940     /**
941      * Add a button to the group (similar to NavItem API.)
942      */
943     addItem : function(cfg)
944     {
945         var cn = new Roo.bootstrap.Button(cfg);
946         //this.register(cn);
947         cn.parentId = this.id;
948         cn.onRender(this.el, null);
949         return cn;
950     }
951    
952 });
953
954  /*
955  * - LGPL
956  *
957  * button
958  * 
959  */
960
961 /**
962  * @class Roo.bootstrap.Button
963  * @extends Roo.bootstrap.Component
964  * Bootstrap Button class
965  * @cfg {String} html The button content
966  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
967  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
968  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
969  * @cfg {String} size (lg|sm|xs)
970  * @cfg {String} tag (a|input|submit)
971  * @cfg {String} href empty or href
972  * @cfg {Boolean} disabled default false;
973  * @cfg {Boolean} isClose default false;
974  * @cfg {String} glyphicon depricated - use fa
975  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
976  * @cfg {String} badge text for badge
977  * @cfg {String} theme (default|glow)  
978  * @cfg {Boolean} inverse dark themed version
979  * @cfg {Boolean} toggle is it a slidy toggle button
980  * @cfg {Boolean} pressed   default null - if the button ahs active state
981  * @cfg {String} ontext text for on slidy toggle state
982  * @cfg {String} offtext text for off slidy toggle state
983  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
984  * @cfg {Boolean} removeClass remove the standard class..
985  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
986  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
987  * 
988  * @constructor
989  * Create a new button
990  * @param {Object} config The config object
991  */
992
993
994 Roo.bootstrap.Button = function(config){
995     Roo.bootstrap.Button.superclass.constructor.call(this, config);
996     
997     this.addEvents({
998         // raw events
999         /**
1000          * @event click
1001          * When a button is pressed
1002          * @param {Roo.bootstrap.Button} btn
1003          * @param {Roo.EventObject} e
1004          */
1005         "click" : true,
1006         /**
1007          * @event dblclick
1008          * When a button is double clicked
1009          * @param {Roo.bootstrap.Button} btn
1010          * @param {Roo.EventObject} e
1011          */
1012         "dblclick" : true,
1013          /**
1014          * @event toggle
1015          * After the button has been toggles
1016          * @param {Roo.bootstrap.Button} btn
1017          * @param {Roo.EventObject} e
1018          * @param {boolean} pressed (also available as button.pressed)
1019          */
1020         "toggle" : true
1021     });
1022 };
1023
1024 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1025     html: false,
1026     active: false,
1027     weight: '',
1028     badge_weight: '',
1029     outline : false,
1030     size: '',
1031     tag: 'button',
1032     href: '',
1033     disabled: false,
1034     isClose: false,
1035     glyphicon: '',
1036     fa: '',
1037     badge: '',
1038     theme: 'default',
1039     inverse: false,
1040     
1041     toggle: false,
1042     ontext: 'ON',
1043     offtext: 'OFF',
1044     defaulton: true,
1045     preventDefault: true,
1046     removeClass: false,
1047     name: false,
1048     target: false,
1049     group : false,
1050      
1051     pressed : null,
1052      
1053     
1054     getAutoCreate : function(){
1055         
1056         var cfg = {
1057             tag : 'button',
1058             cls : 'roo-button',
1059             html: ''
1060         };
1061         
1062         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1063             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1064             this.tag = 'button';
1065         } else {
1066             cfg.tag = this.tag;
1067         }
1068         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1069         
1070         if (this.toggle == true) {
1071             cfg={
1072                 tag: 'div',
1073                 cls: 'slider-frame roo-button',
1074                 cn: [
1075                     {
1076                         tag: 'span',
1077                         'data-on-text':'ON',
1078                         'data-off-text':'OFF',
1079                         cls: 'slider-button',
1080                         html: this.offtext
1081                     }
1082                 ]
1083             };
1084             // why are we validating the weights?
1085             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1086                 cfg.cls +=  ' ' + this.weight;
1087             }
1088             
1089             return cfg;
1090         }
1091         
1092         if (this.isClose) {
1093             cfg.cls += ' close';
1094             
1095             cfg["aria-hidden"] = true;
1096             
1097             cfg.html = "&times;";
1098             
1099             return cfg;
1100         }
1101              
1102         
1103         if (this.theme==='default') {
1104             cfg.cls = 'btn roo-button';
1105             
1106             //if (this.parentType != 'Navbar') {
1107             this.weight = this.weight.length ?  this.weight : 'default';
1108             //}
1109             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1110                 
1111                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1112                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1113                 cfg.cls += ' btn-' + outline + weight;
1114                 if (this.weight == 'default') {
1115                     // BC
1116                     cfg.cls += ' btn-' + this.weight;
1117                 }
1118             }
1119         } else if (this.theme==='glow') {
1120             
1121             cfg.tag = 'a';
1122             cfg.cls = 'btn-glow roo-button';
1123             
1124             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1125                 
1126                 cfg.cls += ' ' + this.weight;
1127             }
1128         }
1129    
1130         
1131         if (this.inverse) {
1132             this.cls += ' inverse';
1133         }
1134         
1135         
1136         if (this.active || this.pressed === true) {
1137             cfg.cls += ' active';
1138         }
1139         
1140         if (this.disabled) {
1141             cfg.disabled = 'disabled';
1142         }
1143         
1144         if (this.items) {
1145             Roo.log('changing to ul' );
1146             cfg.tag = 'ul';
1147             this.glyphicon = 'caret';
1148             if (Roo.bootstrap.version == 4) {
1149                 this.fa = 'caret-down';
1150             }
1151             
1152         }
1153         
1154         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1155          
1156         //gsRoo.log(this.parentType);
1157         if (this.parentType === 'Navbar' && !this.parent().bar) {
1158             Roo.log('changing to li?');
1159             
1160             cfg.tag = 'li';
1161             
1162             cfg.cls = '';
1163             cfg.cn =  [{
1164                 tag : 'a',
1165                 cls : 'roo-button',
1166                 html : this.html,
1167                 href : this.href || '#'
1168             }];
1169             if (this.menu) {
1170                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1171                 cfg.cls += ' dropdown';
1172             }   
1173             
1174             delete cfg.html;
1175             
1176         }
1177         
1178        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1179         
1180         if (this.glyphicon) {
1181             cfg.html = ' ' + cfg.html;
1182             
1183             cfg.cn = [
1184                 {
1185                     tag: 'span',
1186                     cls: 'glyphicon glyphicon-' + this.glyphicon
1187                 }
1188             ];
1189         }
1190         if (this.fa) {
1191             cfg.html = ' ' + cfg.html;
1192             
1193             cfg.cn = [
1194                 {
1195                     tag: 'i',
1196                     cls: 'fa fas fa-' + this.fa
1197                 }
1198             ];
1199         }
1200         
1201         if (this.badge) {
1202             cfg.html += ' ';
1203             
1204             cfg.tag = 'a';
1205             
1206 //            cfg.cls='btn roo-button';
1207             
1208             cfg.href=this.href;
1209             
1210             var value = cfg.html;
1211             
1212             if(this.glyphicon){
1213                 value = {
1214                     tag: 'span',
1215                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1216                     html: this.html
1217                 };
1218             }
1219             if(this.fa){
1220                 value = {
1221                     tag: 'i',
1222                     cls: 'fa fas fa-' + this.fa,
1223                     html: this.html
1224                 };
1225             }
1226             
1227             var bw = this.badge_weight.length ? this.badge_weight :
1228                 (this.weight.length ? this.weight : 'secondary');
1229             bw = bw == 'default' ? 'secondary' : bw;
1230             
1231             cfg.cn = [
1232                 value,
1233                 {
1234                     tag: 'span',
1235                     cls: 'badge badge-' + bw,
1236                     html: this.badge
1237                 }
1238             ];
1239             
1240             cfg.html='';
1241         }
1242         
1243         if (this.menu) {
1244             cfg.cls += ' dropdown';
1245             cfg.html = typeof(cfg.html) != 'undefined' ?
1246                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1247         }
1248         
1249         if (cfg.tag !== 'a' && this.href !== '') {
1250             throw "Tag must be a to set href.";
1251         } else if (this.href.length > 0) {
1252             cfg.href = this.href;
1253         }
1254         
1255         if(this.removeClass){
1256             cfg.cls = '';
1257         }
1258         
1259         if(this.target){
1260             cfg.target = this.target;
1261         }
1262         
1263         return cfg;
1264     },
1265     initEvents: function() {
1266        // Roo.log('init events?');
1267 //        Roo.log(this.el.dom);
1268         // add the menu...
1269         
1270         if (typeof (this.menu) != 'undefined') {
1271             this.menu.parentType = this.xtype;
1272             this.menu.triggerEl = this.el;
1273             this.addxtype(Roo.apply({}, this.menu));
1274         }
1275
1276
1277         if (this.el.hasClass('roo-button')) {
1278              this.el.on('click', this.onClick, this);
1279              this.el.on('dblclick', this.onDblClick, this);
1280         } else {
1281              this.el.select('.roo-button').on('click', this.onClick, this);
1282              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1283              
1284         }
1285         // why?
1286         if(this.removeClass){
1287             this.el.on('click', this.onClick, this);
1288         }
1289         
1290         if (this.group === true) {
1291              if (this.pressed === false || this.pressed === true) {
1292                 // nothing
1293             } else {
1294                 this.pressed = false;
1295                 this.setActive(this.pressed);
1296             }
1297             
1298         }
1299         
1300         this.el.enableDisplayMode();
1301         
1302     },
1303     onClick : function(e)
1304     {
1305         if (this.disabled) {
1306             return;
1307         }
1308         
1309         Roo.log('button on click ');
1310         if(this.preventDefault){
1311             e.preventDefault();
1312         }
1313         
1314         if (this.group) {
1315             if (this.pressed) {
1316                 // do nothing -
1317                 return;
1318             }
1319             this.setActive(true);
1320             var pi = this.parent().items;
1321             for (var i = 0;i < pi.length;i++) {
1322                 if (this == pi[i]) {
1323                     continue;
1324                 }
1325                 if (pi[i].el.hasClass('roo-button')) {
1326                     pi[i].setActive(false);
1327                 }
1328             }
1329             this.fireEvent('click', this, e);            
1330             return;
1331         }
1332         
1333         if (this.pressed === true || this.pressed === false) {
1334             this.toggleActive(e);
1335         }
1336         
1337         
1338         this.fireEvent('click', this, e);
1339     },
1340     onDblClick: function(e)
1341     {
1342         if (this.disabled) {
1343             return;
1344         }
1345         if(this.preventDefault){
1346             e.preventDefault();
1347         }
1348         this.fireEvent('dblclick', this, e);
1349     },
1350     /**
1351      * Enables this button
1352      */
1353     enable : function()
1354     {
1355         this.disabled = false;
1356         this.el.removeClass('disabled');
1357     },
1358     
1359     /**
1360      * Disable this button
1361      */
1362     disable : function()
1363     {
1364         this.disabled = true;
1365         this.el.addClass('disabled');
1366     },
1367      /**
1368      * sets the active state on/off, 
1369      * @param {Boolean} state (optional) Force a particular state
1370      */
1371     setActive : function(v) {
1372         
1373         this.el[v ? 'addClass' : 'removeClass']('active');
1374         this.pressed = v;
1375     },
1376      /**
1377      * toggles the current active state 
1378      */
1379     toggleActive : function(e)
1380     {
1381         this.setActive(!this.pressed); // this modifies pressed...
1382         this.fireEvent('toggle', this, e, this.pressed);
1383     },
1384      /**
1385      * get the current active state
1386      * @return {boolean} true if it's active
1387      */
1388     isActive : function()
1389     {
1390         return this.el.hasClass('active');
1391     },
1392     /**
1393      * set the text of the first selected button
1394      */
1395     setText : function(str)
1396     {
1397         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1398     },
1399     /**
1400      * get the text of the first selected button
1401      */
1402     getText : function()
1403     {
1404         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1405     },
1406     
1407     setWeight : function(str)
1408     {
1409         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1410         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1411         this.weight = str;
1412         var outline = this.outline ? 'outline-' : '';
1413         if (str == 'default') {
1414             this.el.addClass('btn-default btn-outline-secondary');        
1415             return;
1416         }
1417         this.el.addClass('btn-' + outline + str);        
1418     }
1419     
1420     
1421 });
1422 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1423
1424 Roo.bootstrap.Button.weights = [
1425     'default',
1426     'secondary' ,
1427     'primary',
1428     'success',
1429     'info',
1430     'warning',
1431     'danger',
1432     'link',
1433     'light',
1434     'dark'              
1435    
1436 ];/*
1437  * - LGPL
1438  *
1439  * column
1440  * 
1441  */
1442
1443 /**
1444  * @class Roo.bootstrap.Column
1445  * @extends Roo.bootstrap.Component
1446  * Bootstrap Column class
1447  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1448  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1449  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1450  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1451  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1452  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1453  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1454  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1455  *
1456  * 
1457  * @cfg {Boolean} hidden (true|false) hide the element
1458  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1459  * @cfg {String} fa (ban|check|...) font awesome icon
1460  * @cfg {Number} fasize (1|2|....) font awsome size
1461
1462  * @cfg {String} icon (info-sign|check|...) glyphicon name
1463
1464  * @cfg {String} html content of column.
1465  * 
1466  * @constructor
1467  * Create a new Column
1468  * @param {Object} config The config object
1469  */
1470
1471 Roo.bootstrap.Column = function(config){
1472     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1473 };
1474
1475 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1476     
1477     xs: false,
1478     sm: false,
1479     md: false,
1480     lg: false,
1481     xsoff: false,
1482     smoff: false,
1483     mdoff: false,
1484     lgoff: false,
1485     html: '',
1486     offset: 0,
1487     alert: false,
1488     fa: false,
1489     icon : false,
1490     hidden : false,
1491     fasize : 1,
1492     
1493     getAutoCreate : function(){
1494         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1495         
1496         cfg = {
1497             tag: 'div',
1498             cls: 'column'
1499         };
1500         
1501         var settings=this;
1502         var sizes =   ['xs','sm','md','lg'];
1503         sizes.map(function(size ,ix){
1504             //Roo.log( size + ':' + settings[size]);
1505             
1506             if (settings[size+'off'] !== false) {
1507                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1508             }
1509             
1510             if (settings[size] === false) {
1511                 return;
1512             }
1513             
1514             if (!settings[size]) { // 0 = hidden
1515                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1516                 // bootsrap4
1517                 for (var i = ix; i > -1; i--) {
1518                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1519                 }
1520                 
1521                 
1522                 return;
1523             }
1524             cfg.cls += ' col-' + size + '-' + settings[size] + (
1525                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1526             );
1527             
1528         });
1529         
1530         if (this.hidden) {
1531             cfg.cls += ' hidden';
1532         }
1533         
1534         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1535             cfg.cls +=' alert alert-' + this.alert;
1536         }
1537         
1538         
1539         if (this.html.length) {
1540             cfg.html = this.html;
1541         }
1542         if (this.fa) {
1543             var fasize = '';
1544             if (this.fasize > 1) {
1545                 fasize = ' fa-' + this.fasize + 'x';
1546             }
1547             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1548             
1549             
1550         }
1551         if (this.icon) {
1552             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1553         }
1554         
1555         return cfg;
1556     }
1557    
1558 });
1559
1560  
1561
1562  /*
1563  * - LGPL
1564  *
1565  * page container.
1566  * 
1567  */
1568
1569
1570 /**
1571  * @class Roo.bootstrap.Container
1572  * @extends Roo.bootstrap.Component
1573  * Bootstrap Container class
1574  * @cfg {Boolean} jumbotron is it a jumbotron element
1575  * @cfg {String} html content of element
1576  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1577  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1578  * @cfg {String} header content of header (for panel)
1579  * @cfg {String} footer content of footer (for panel)
1580  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1581  * @cfg {String} tag (header|aside|section) type of HTML tag.
1582  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1583  * @cfg {String} fa font awesome icon
1584  * @cfg {String} icon (info-sign|check|...) glyphicon name
1585  * @cfg {Boolean} hidden (true|false) hide the element
1586  * @cfg {Boolean} expandable (true|false) default false
1587  * @cfg {Boolean} expanded (true|false) default true
1588  * @cfg {String} rheader contet on the right of header
1589  * @cfg {Boolean} clickable (true|false) default false
1590
1591  *     
1592  * @constructor
1593  * Create a new Container
1594  * @param {Object} config The config object
1595  */
1596
1597 Roo.bootstrap.Container = function(config){
1598     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1599     
1600     this.addEvents({
1601         // raw events
1602          /**
1603          * @event expand
1604          * After the panel has been expand
1605          * 
1606          * @param {Roo.bootstrap.Container} this
1607          */
1608         "expand" : true,
1609         /**
1610          * @event collapse
1611          * After the panel has been collapsed
1612          * 
1613          * @param {Roo.bootstrap.Container} this
1614          */
1615         "collapse" : true,
1616         /**
1617          * @event click
1618          * When a element is chick
1619          * @param {Roo.bootstrap.Container} this
1620          * @param {Roo.EventObject} e
1621          */
1622         "click" : true
1623     });
1624 };
1625
1626 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1627     
1628     jumbotron : false,
1629     well: '',
1630     panel : '',
1631     header: '',
1632     footer : '',
1633     sticky: '',
1634     tag : false,
1635     alert : false,
1636     fa: false,
1637     icon : false,
1638     expandable : false,
1639     rheader : '',
1640     expanded : true,
1641     clickable: false,
1642   
1643      
1644     getChildContainer : function() {
1645         
1646         if(!this.el){
1647             return false;
1648         }
1649         
1650         if (this.panel.length) {
1651             return this.el.select('.panel-body',true).first();
1652         }
1653         
1654         return this.el;
1655     },
1656     
1657     
1658     getAutoCreate : function(){
1659         
1660         var cfg = {
1661             tag : this.tag || 'div',
1662             html : '',
1663             cls : ''
1664         };
1665         if (this.jumbotron) {
1666             cfg.cls = 'jumbotron';
1667         }
1668         
1669         
1670         
1671         // - this is applied by the parent..
1672         //if (this.cls) {
1673         //    cfg.cls = this.cls + '';
1674         //}
1675         
1676         if (this.sticky.length) {
1677             
1678             var bd = Roo.get(document.body);
1679             if (!bd.hasClass('bootstrap-sticky')) {
1680                 bd.addClass('bootstrap-sticky');
1681                 Roo.select('html',true).setStyle('height', '100%');
1682             }
1683              
1684             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1685         }
1686         
1687         
1688         if (this.well.length) {
1689             switch (this.well) {
1690                 case 'lg':
1691                 case 'sm':
1692                     cfg.cls +=' well well-' +this.well;
1693                     break;
1694                 default:
1695                     cfg.cls +=' well';
1696                     break;
1697             }
1698         }
1699         
1700         if (this.hidden) {
1701             cfg.cls += ' hidden';
1702         }
1703         
1704         
1705         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1706             cfg.cls +=' alert alert-' + this.alert;
1707         }
1708         
1709         var body = cfg;
1710         
1711         if (this.panel.length) {
1712             cfg.cls += ' panel panel-' + this.panel;
1713             cfg.cn = [];
1714             if (this.header.length) {
1715                 
1716                 var h = [];
1717                 
1718                 if(this.expandable){
1719                     
1720                     cfg.cls = cfg.cls + ' expandable';
1721                     
1722                     h.push({
1723                         tag: 'i',
1724                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1725                     });
1726                     
1727                 }
1728                 
1729                 h.push(
1730                     {
1731                         tag: 'span',
1732                         cls : 'panel-title',
1733                         html : (this.expandable ? '&nbsp;' : '') + this.header
1734                     },
1735                     {
1736                         tag: 'span',
1737                         cls: 'panel-header-right',
1738                         html: this.rheader
1739                     }
1740                 );
1741                 
1742                 cfg.cn.push({
1743                     cls : 'panel-heading',
1744                     style : this.expandable ? 'cursor: pointer' : '',
1745                     cn : h
1746                 });
1747                 
1748             }
1749             
1750             body = false;
1751             cfg.cn.push({
1752                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1753                 html : this.html
1754             });
1755             
1756             
1757             if (this.footer.length) {
1758                 cfg.cn.push({
1759                     cls : 'panel-footer',
1760                     html : this.footer
1761                     
1762                 });
1763             }
1764             
1765         }
1766         
1767         if (body) {
1768             body.html = this.html || cfg.html;
1769             // prefix with the icons..
1770             if (this.fa) {
1771                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1772             }
1773             if (this.icon) {
1774                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1775             }
1776             
1777             
1778         }
1779         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1780             cfg.cls =  'container';
1781         }
1782         
1783         return cfg;
1784     },
1785     
1786     initEvents: function() 
1787     {
1788         if(this.expandable){
1789             var headerEl = this.headerEl();
1790         
1791             if(headerEl){
1792                 headerEl.on('click', this.onToggleClick, this);
1793             }
1794         }
1795         
1796         if(this.clickable){
1797             this.el.on('click', this.onClick, this);
1798         }
1799         
1800     },
1801     
1802     onToggleClick : function()
1803     {
1804         var headerEl = this.headerEl();
1805         
1806         if(!headerEl){
1807             return;
1808         }
1809         
1810         if(this.expanded){
1811             this.collapse();
1812             return;
1813         }
1814         
1815         this.expand();
1816     },
1817     
1818     expand : function()
1819     {
1820         if(this.fireEvent('expand', this)) {
1821             
1822             this.expanded = true;
1823             
1824             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1825             
1826             this.el.select('.panel-body',true).first().removeClass('hide');
1827             
1828             var toggleEl = this.toggleEl();
1829
1830             if(!toggleEl){
1831                 return;
1832             }
1833
1834             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1835         }
1836         
1837     },
1838     
1839     collapse : function()
1840     {
1841         if(this.fireEvent('collapse', this)) {
1842             
1843             this.expanded = false;
1844             
1845             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1846             this.el.select('.panel-body',true).first().addClass('hide');
1847         
1848             var toggleEl = this.toggleEl();
1849
1850             if(!toggleEl){
1851                 return;
1852             }
1853
1854             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1855         }
1856     },
1857     
1858     toggleEl : function()
1859     {
1860         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1861             return;
1862         }
1863         
1864         return this.el.select('.panel-heading .fa',true).first();
1865     },
1866     
1867     headerEl : function()
1868     {
1869         if(!this.el || !this.panel.length || !this.header.length){
1870             return;
1871         }
1872         
1873         return this.el.select('.panel-heading',true).first()
1874     },
1875     
1876     bodyEl : function()
1877     {
1878         if(!this.el || !this.panel.length){
1879             return;
1880         }
1881         
1882         return this.el.select('.panel-body',true).first()
1883     },
1884     
1885     titleEl : function()
1886     {
1887         if(!this.el || !this.panel.length || !this.header.length){
1888             return;
1889         }
1890         
1891         return this.el.select('.panel-title',true).first();
1892     },
1893     
1894     setTitle : function(v)
1895     {
1896         var titleEl = this.titleEl();
1897         
1898         if(!titleEl){
1899             return;
1900         }
1901         
1902         titleEl.dom.innerHTML = v;
1903     },
1904     
1905     getTitle : function()
1906     {
1907         
1908         var titleEl = this.titleEl();
1909         
1910         if(!titleEl){
1911             return '';
1912         }
1913         
1914         return titleEl.dom.innerHTML;
1915     },
1916     
1917     setRightTitle : function(v)
1918     {
1919         var t = this.el.select('.panel-header-right',true).first();
1920         
1921         if(!t){
1922             return;
1923         }
1924         
1925         t.dom.innerHTML = v;
1926     },
1927     
1928     onClick : function(e)
1929     {
1930         e.preventDefault();
1931         
1932         this.fireEvent('click', this, e);
1933     }
1934 });
1935
1936  /*
1937  *  - LGPL
1938  *
1939  *  This is BS4's Card element.. - similar to our containers probably..
1940  * 
1941  */
1942 /**
1943  * @class Roo.bootstrap.Card
1944  * @extends Roo.bootstrap.Component
1945  * Bootstrap Card class
1946  *
1947  *
1948  * possible... may not be implemented..
1949  * @cfg {String} header_image  src url of image.
1950  * @cfg {String|Object} header
1951  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1952  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1953  * 
1954  * @cfg {String} title
1955  * @cfg {String} subtitle
1956  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1957  * @cfg {String} footer
1958  
1959  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1960  * 
1961  * @cfg {String} margin (0|1|2|3|4|5|auto)
1962  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1963  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1964  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1965  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1966  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1967  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1968  *
1969  * @cfg {String} padding (0|1|2|3|4|5)
1970  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1971  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1972  * @cfg {String} padding_left (0|1|2|3|4|5)
1973  * @cfg {String} padding_right (0|1|2|3|4|5)
1974  * @cfg {String} padding_x (0|1|2|3|4|5)
1975  * @cfg {String} padding_y (0|1|2|3|4|5)
1976  *
1977  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1978  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1979  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1980  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1981  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1982  
1983  * @config {Boolean} dragable  if this card can be dragged.
1984  * @config {String} drag_group  group for drag
1985  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
1986  * @config {String} drop_group  group for drag
1987  * 
1988  * @config {Boolean} collapsable can the body be collapsed.
1989  * @config {Boolean} collapsed is the body collapsed when rendered...
1990  * @config {Boolean} rotateable can the body be rotated by clicking on it..
1991  * @config {Boolean} rotated is the body rotated when rendered...
1992  * 
1993  * @constructor
1994  * Create a new Container
1995  * @param {Object} config The config object
1996  */
1997
1998 Roo.bootstrap.Card = function(config){
1999     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2000     
2001     this.addEvents({
2002          // raw events
2003         /**
2004          * @event drop
2005          * When a element a card is dropped
2006          * @param {Roo.bootstrap.Card} this
2007          *
2008          * 
2009          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2010          * @param {String} position 'above' or 'below'
2011          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2012         
2013          */
2014         'drop' : true,
2015          /**
2016          * @event rotate
2017          * When a element a card is rotate
2018          * @param {Roo.bootstrap.Element} this
2019          * @param {Roo.Element} n the node being dropped?
2020          * @param {Boolean} rotate status
2021          */
2022         'rotate' : true
2023         
2024     });
2025 };
2026
2027
2028 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2029     
2030     
2031     weight : '',
2032     
2033     margin: '', /// may be better in component?
2034     margin_top: '', 
2035     margin_bottom: '', 
2036     margin_left: '',
2037     margin_right: '',
2038     margin_x: '',
2039     margin_y: '',
2040     
2041     padding : '',
2042     padding_top: '', 
2043     padding_bottom: '', 
2044     padding_left: '',
2045     padding_right: '',
2046     padding_x: '',
2047     padding_y: '',
2048     
2049     display: '', 
2050     display_xs: '', 
2051     display_sm: '', 
2052     display_lg: '',
2053     display_xl: '',
2054  
2055     header_image  : '',
2056     header : '',
2057     header_size : 0,
2058     title : '',
2059     subtitle : '',
2060     html : '',
2061     footer: '',
2062
2063     collapsable : false,
2064     collapsed : false,
2065     rotateable : false,
2066     rotated : false,
2067     
2068     dragable : false,
2069     drag_group : false,
2070     dropable : false,
2071     drop_group : false,
2072     childContainer : false,
2073     dropEl : false, /// the dom placeholde element that indicates drop location.
2074     containerEl: false, // body container
2075     bodyEl: false, // card-body
2076     headerContainerEl : false, //
2077     headerEl : false,
2078     
2079     layoutCls : function()
2080     {
2081         var cls = '';
2082         var t = this;
2083         Roo.log(this.margin_bottom.length);
2084         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2085             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2086             
2087             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2088                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2089             }
2090             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2091                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2092             }
2093         });
2094         
2095         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2096             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2097                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2098             }
2099         });
2100         
2101         // more generic support?
2102         if (this.hidden) {
2103             cls += ' d-none';
2104         }
2105         
2106         return cls;
2107     },
2108  
2109        // Roo.log("Call onRender: " + this.xtype);
2110         /*  We are looking at something like this.
2111 <div class="card">
2112     <img src="..." class="card-img-top" alt="...">
2113     <div class="card-body">
2114         <h5 class="card-title">Card title</h5>
2115          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2116
2117         >> this bit is really the body...
2118         <div> << we will ad dthis in hopefully it will not break shit.
2119         
2120         ** card text does not actually have any styling...
2121         
2122             <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2123         
2124         </div> <<
2125           <a href="#" class="card-link">Card link</a>
2126           
2127     </div>
2128     <div class="card-footer">
2129         <small class="text-muted">Last updated 3 mins ago</small>
2130     </div>
2131 </div>
2132          */
2133     getAutoCreate : function(){
2134         
2135         var cfg = {
2136             tag : 'div',
2137             cls : 'card',
2138             cn : [ ]
2139         };
2140         
2141         if (this.weight.length && this.weight != 'light') {
2142             cfg.cls += ' text-white';
2143         } else {
2144             cfg.cls += ' text-dark'; // need as it's nested..
2145         }
2146         if (this.weight.length) {
2147             cfg.cls += ' bg-' + this.weight;
2148         }
2149         
2150         cfg.cls += ' ' + this.layoutCls(); 
2151         
2152         var hdr = false;
2153         var hdr_ctr = false;
2154         if (this.header.length) {
2155             hdr = {
2156                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2157                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2158                 cn : []
2159             };
2160             cfg.cn.push(hdr);
2161             hdr_ctr = hdr;
2162         } else {
2163             hdr = {
2164                 tag : 'div',
2165                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2166                 cn : []
2167             };
2168             cfg.cn.push(hdr);
2169             hdr_ctr = hdr;
2170         }
2171         if (this.collapsable) {
2172             hdr_ctr = {
2173             tag : 'a',
2174             cls : 'd-block user-select-none',
2175             cn: [
2176                     {
2177                         tag: 'i',
2178                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2179                     }
2180                    
2181                 ]
2182             };
2183             hdr.cn.push(hdr_ctr);
2184         }
2185         
2186         hdr_ctr.cn.push(        {
2187             tag: 'span',
2188             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2189             html : this.header
2190         });
2191         
2192         
2193         if (this.header_image.length) {
2194             cfg.cn.push({
2195                 tag : 'img',
2196                 cls : 'card-img-top',
2197                 src: this.header_image // escape?
2198             });
2199         } else {
2200             cfg.cn.push({
2201                     tag : 'div',
2202                     cls : 'card-img-top d-none' 
2203                 });
2204         }
2205             
2206         var body = {
2207             tag : 'div',
2208             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2209             cn : []
2210         };
2211         var obody = body;
2212         if (this.collapsable || this.rotateable) {
2213             obody = {
2214                 tag: 'div',
2215                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2216                 cn : [  body ]
2217             };
2218         }
2219         
2220         cfg.cn.push(obody);
2221         
2222         if (this.title.length) {
2223             body.cn.push({
2224                 tag : 'div',
2225                 cls : 'card-title',
2226                 src: this.title // escape?
2227             });
2228         }  
2229         
2230         if (this.subtitle.length) {
2231             body.cn.push({
2232                 tag : 'div',
2233                 cls : 'card-title',
2234                 src: this.subtitle // escape?
2235             });
2236         }
2237         
2238         body.cn.push({
2239             tag : 'div',
2240             cls : 'roo-card-body-ctr'
2241         });
2242         
2243         if (this.html.length) {
2244             body.cn.push({
2245                 tag: 'div',
2246                 html : this.html
2247             });
2248         }
2249         // fixme ? handle objects?
2250         
2251         if (this.footer.length) {
2252            
2253             cfg.cn.push({
2254                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2255                 html : this.footer
2256             });
2257             
2258         } else {
2259             cfg.cn.push({cls : 'card-footer d-none'});
2260         }
2261         
2262         // footer...
2263         
2264         return cfg;
2265     },
2266     
2267     
2268     getCardHeader : function()
2269     {
2270         var  ret = this.el.select('.card-header',true).first();
2271         if (ret.hasClass('d-none')) {
2272             ret.removeClass('d-none');
2273         }
2274         
2275         return ret;
2276     },
2277     getCardFooter : function()
2278     {
2279         var  ret = this.el.select('.card-footer',true).first();
2280         if (ret.hasClass('d-none')) {
2281             ret.removeClass('d-none');
2282         }
2283         
2284         return ret;
2285     },
2286     getCardImageTop : function()
2287     {
2288         var  ret = this.el.select('.card-img-top',true).first();
2289         if (ret.hasClass('d-none')) {
2290             ret.removeClass('d-none');
2291         }
2292             
2293         return ret;
2294     },
2295     
2296     getChildContainer : function()
2297     {
2298         
2299         if(!this.el){
2300             return false;
2301         }
2302         return this.el.select('.roo-card-body-ctr',true).first();    
2303     },
2304     
2305     initEvents: function() 
2306     {
2307         this.bodyEl = this.el.select('.card-body',true).first(); 
2308         this.containerEl = this.getChildContainer();
2309         if(this.dragable){
2310             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2311                     containerScroll: true,
2312                     ddGroup: this.drag_group || 'default_card_drag_group'
2313             });
2314             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2315         }
2316         if (this.dropable) {
2317             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2318                 containerScroll: true,
2319                 ddGroup: this.drop_group || 'default_card_drag_group'
2320             });
2321             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2322             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2323             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2324             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2325             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2326         }
2327         
2328         if (this.collapsable) {
2329             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2330         }
2331         if (this.rotateable) {
2332             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2333         }
2334         this.collapsableEl = this.el.select('.roo-collapsable').first();
2335          
2336         this.footerEl = this.el.select('.card-footer').first();
2337         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2338         this.headerContainerEl = this.el.select('.roo-card-header-ctr').first();
2339         this.headerEl = this.el.select('.card-header',true).first();
2340         
2341         if (this.rotated) {
2342             this.el.addClass('roo-card-rotated');
2343             this.fireEvent('rotate', this, true);
2344         }
2345         
2346     },
2347     getDragData : function(e)
2348     {
2349         var target = this.getEl();
2350         if (target) {
2351             //this.handleSelection(e);
2352             
2353             var dragData = {
2354                 source: this,
2355                 copy: false,
2356                 nodes: this.getEl(),
2357                 records: []
2358             };
2359             
2360             
2361             dragData.ddel = target.dom ;    // the div element
2362             Roo.log(target.getWidth( ));
2363             dragData.ddel.style.width = target.getWidth() + 'px';
2364             
2365             return dragData;
2366         }
2367         return false;
2368     },
2369     /**
2370     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2371     *    whole Element becomes the target, and this causes the drop gesture to append.
2372     */
2373     getTargetFromEvent : function(e, dragged_card_el)
2374     {
2375         var target = e.getTarget();
2376         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2377             target = target.parentNode;
2378         }
2379         
2380         var ret = {
2381             position: '',
2382             cards : [],
2383             card_n : -1,
2384             items_n : -1,
2385             card : false 
2386         };
2387         
2388         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2389         // see if target is one of the 'cards'...
2390         
2391         
2392         //Roo.log(this.items.length);
2393         var pos = false;
2394         
2395         var last_card_n = 0;
2396         var cards_len  = 0;
2397         for (var i = 0;i< this.items.length;i++) {
2398             
2399             if (!this.items[i].el.hasClass('card')) {
2400                  continue;
2401             }
2402             pos = this.getDropPoint(e, this.items[i].el.dom);
2403             
2404             cards_len = ret.cards.length;
2405             //Roo.log(this.items[i].el.dom.id);
2406             ret.cards.push(this.items[i]);
2407             last_card_n  = i;
2408             if (ret.card_n < 0 && pos == 'above') {
2409                 ret.position = cards_len > 0 ? 'below' : pos;
2410                 ret.items_n = i > 0 ? i - 1 : 0;
2411                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2412                 ret.card = ret.cards[ret.card_n];
2413             }
2414         }
2415         if (!ret.cards.length) {
2416             ret.card = true;
2417             ret.position = 'below';
2418             ret.items_n;
2419             return ret;
2420         }
2421         // could not find a card.. stick it at the end..
2422         if (ret.card_n < 0) {
2423             ret.card_n = last_card_n;
2424             ret.card = ret.cards[last_card_n];
2425             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2426             ret.position = 'below';
2427         }
2428         
2429         if (this.items[ret.items_n].el == dragged_card_el) {
2430             return false;
2431         }
2432         
2433         if (ret.position == 'below') {
2434             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2435             
2436             if (card_after  && card_after.el == dragged_card_el) {
2437                 return false;
2438             }
2439             return ret;
2440         }
2441         
2442         // its's after ..
2443         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2444         
2445         if (card_before  && card_before.el == dragged_card_el) {
2446             return false;
2447         }
2448         
2449         return ret;
2450     },
2451     
2452     onNodeEnter : function(n, dd, e, data){
2453         return false;
2454     },
2455     onNodeOver : function(n, dd, e, data)
2456     {
2457        
2458         var target_info = this.getTargetFromEvent(e,data.source.el);
2459         if (target_info === false) {
2460             this.dropPlaceHolder('hide');
2461             return false;
2462         }
2463         Roo.log(['getTargetFromEvent', target_info ]);
2464         
2465          
2466         this.dropPlaceHolder('show', target_info,data);
2467         
2468         return false; 
2469     },
2470     onNodeOut : function(n, dd, e, data){
2471         this.dropPlaceHolder('hide');
2472      
2473     },
2474     onNodeDrop : function(n, dd, e, data)
2475     {
2476         
2477         // call drop - return false if
2478         
2479         // this could actually fail - if the Network drops..
2480         // we will ignore this at present..- client should probably reload
2481         // the whole set of cards if stuff like that fails.
2482         
2483         
2484         var info = this.getTargetFromEvent(e,data.source.el);
2485         if (info === false) {
2486             return false;
2487         }
2488         this.dropPlaceHolder('hide');
2489   
2490          
2491     
2492     
2493     
2494         this.acceptCard(data.source, info.position, info.card, info.items_n);
2495         return true;
2496          
2497     },
2498     firstChildCard : function()
2499     {
2500         for (var i = 0;i< this.items.length;i++) {
2501             
2502             if (!this.items[i].el.hasClass('card')) {
2503                  continue;
2504             }
2505             return this.items[i];
2506         }
2507         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2508     },
2509     /**
2510      * accept card
2511      *
2512      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2513      */
2514     acceptCard : function(move_card,  position, next_to_card )
2515     {
2516         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2517             return false;
2518         }
2519         
2520         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2521         
2522         move_card.parent().removeCard(move_card);
2523         
2524         
2525         var dom = move_card.el.dom;
2526         dom.style.width = ''; // clear with - which is set by drag.
2527         
2528         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2529             var cardel = next_to_card.el.dom;
2530             
2531             if (position == 'above' ) {
2532                 cardel.parentNode.insertBefore(dom, cardel);
2533             } else if (cardel.nextSibling) {
2534                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2535             } else {
2536                 cardel.parentNode.append(dom);
2537             }
2538         } else {
2539             // card container???
2540             this.containerEl.dom.append(dom);
2541         }
2542         
2543         //FIXME HANDLE card = true 
2544         
2545         // add this to the correct place in items.
2546         
2547         // remove Card from items.
2548         
2549        
2550         if (this.items.length) {
2551             var nitems = [];
2552             //Roo.log([info.items_n, info.position, this.items.length]);
2553             for (var i =0; i < this.items.length; i++) {
2554                 if (i == to_items_n && position == 'above') {
2555                     nitems.push(move_card);
2556                 }
2557                 nitems.push(this.items[i]);
2558                 if (i == to_items_n && position == 'below') {
2559                     nitems.push(move_card);
2560                 }
2561             }
2562             this.items = nitems;
2563             Roo.log(this.items);
2564         } else {
2565             this.items.push(move_card);
2566         }
2567         
2568         move_card.parentId = this.id;
2569         
2570         return true;
2571         
2572         
2573     },
2574     removeCard : function(c)
2575     {
2576         this.items = this.items.filter(function(e) { return e != c });
2577  
2578         var dom = c.el.dom;
2579         dom.parentNode.removeChild(dom);
2580         dom.style.width = ''; // clear with - which is set by drag.
2581         c.parentId = false;
2582         
2583     },
2584     
2585     /**    Decide whether to drop above or below a View node. */
2586     getDropPoint : function(e, n, dd)
2587     {
2588         if (dd) {
2589              return false;
2590         }
2591         if (n == this.containerEl.dom) {
2592             return "above";
2593         }
2594         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2595         var c = t + (b - t) / 2;
2596         var y = Roo.lib.Event.getPageY(e);
2597         if(y <= c) {
2598             return "above";
2599         }else{
2600             return "below";
2601         }
2602     },
2603     onToggleCollapse : function(e)
2604         {
2605         if (this.collapsed) {
2606             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2607             this.collapsableEl.addClass('show');
2608             this.collapsed = false;
2609             return;
2610         }
2611         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2612         this.collapsableEl.removeClass('show');
2613         this.collapsed = true;
2614         
2615     
2616     },
2617     
2618     onToggleRotate : function(e)
2619     {
2620         this.collapsableEl.removeClass('show');
2621         this.footerEl.removeClass('d-none');
2622         this.el.removeClass('roo-card-rotated');
2623         this.el.removeClass('d-none');
2624         if (this.rotated) {
2625             
2626             this.collapsableEl.addClass('show');
2627             this.rotated = false;
2628             this.fireEvent('rotate', this, this.rotated);
2629             return;
2630         }
2631         this.el.addClass('roo-card-rotated');
2632         this.footerEl.addClass('d-none');
2633         this.el.select('.roo-collapsable').removeClass('show');
2634         
2635         this.rotated = true;
2636         this.fireEvent('rotate', this, this.rotated);
2637     
2638     },
2639     
2640     dropPlaceHolder: function (action, info, data)
2641     {
2642         if (this.dropEl === false) {
2643             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2644             cls : 'd-none'
2645             },true);
2646         }
2647         this.dropEl.removeClass(['d-none', 'd-block']);        
2648         if (action == 'hide') {
2649             
2650             this.dropEl.addClass('d-none');
2651             return;
2652         }
2653         // FIXME - info.card == true!!!
2654         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2655         
2656         if (info.card !== true) {
2657             var cardel = info.card.el.dom;
2658             
2659             if (info.position == 'above') {
2660                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2661             } else if (cardel.nextSibling) {
2662                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2663             } else {
2664                 cardel.parentNode.append(this.dropEl.dom);
2665             }
2666         } else {
2667             // card container???
2668             this.containerEl.dom.append(this.dropEl.dom);
2669         }
2670         
2671         this.dropEl.addClass('d-block roo-card-dropzone');
2672         
2673         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2674         
2675         
2676     
2677     
2678     
2679     },
2680     setHeaderText: function(html)
2681     {
2682         this.header = html;
2683         if (this.headerContainerEl) {
2684             this.headerContainerEl.dom.innerHTML = html;
2685         }
2686     }
2687
2688     
2689 });
2690
2691 /*
2692  * - LGPL
2693  *
2694  * Card header - holder for the card header elements.
2695  * 
2696  */
2697
2698 /**
2699  * @class Roo.bootstrap.CardHeader
2700  * @extends Roo.bootstrap.Element
2701  * Bootstrap CardHeader class
2702  * @constructor
2703  * Create a new Card Header - that you can embed children into
2704  * @param {Object} config The config object
2705  */
2706
2707 Roo.bootstrap.CardHeader = function(config){
2708     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2709 };
2710
2711 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2712     
2713     
2714     container_method : 'getCardHeader' 
2715     
2716      
2717     
2718     
2719    
2720 });
2721
2722  
2723
2724  /*
2725  * - LGPL
2726  *
2727  * Card footer - holder for the card footer elements.
2728  * 
2729  */
2730
2731 /**
2732  * @class Roo.bootstrap.CardFooter
2733  * @extends Roo.bootstrap.Element
2734  * Bootstrap CardFooter class
2735  * @constructor
2736  * Create a new Card Footer - that you can embed children into
2737  * @param {Object} config The config object
2738  */
2739
2740 Roo.bootstrap.CardFooter = function(config){
2741     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2742 };
2743
2744 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2745     
2746     
2747     container_method : 'getCardFooter' 
2748     
2749      
2750     
2751     
2752    
2753 });
2754
2755  
2756
2757  /*
2758  * - LGPL
2759  *
2760  * Card header - holder for the card header elements.
2761  * 
2762  */
2763
2764 /**
2765  * @class Roo.bootstrap.CardImageTop
2766  * @extends Roo.bootstrap.Element
2767  * Bootstrap CardImageTop class
2768  * @constructor
2769  * Create a new Card Image Top container
2770  * @param {Object} config The config object
2771  */
2772
2773 Roo.bootstrap.CardImageTop = function(config){
2774     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2775 };
2776
2777 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2778     
2779    
2780     container_method : 'getCardImageTop' 
2781     
2782      
2783     
2784    
2785 });
2786
2787  
2788
2789  /*
2790  * - LGPL
2791  *
2792  * image
2793  * 
2794  */
2795
2796
2797 /**
2798  * @class Roo.bootstrap.Img
2799  * @extends Roo.bootstrap.Component
2800  * Bootstrap Img class
2801  * @cfg {Boolean} imgResponsive false | true
2802  * @cfg {String} border rounded | circle | thumbnail
2803  * @cfg {String} src image source
2804  * @cfg {String} alt image alternative text
2805  * @cfg {String} href a tag href
2806  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2807  * @cfg {String} xsUrl xs image source
2808  * @cfg {String} smUrl sm image source
2809  * @cfg {String} mdUrl md image source
2810  * @cfg {String} lgUrl lg image source
2811  * 
2812  * @constructor
2813  * Create a new Input
2814  * @param {Object} config The config object
2815  */
2816
2817 Roo.bootstrap.Img = function(config){
2818     Roo.bootstrap.Img.superclass.constructor.call(this, config);
2819     
2820     this.addEvents({
2821         // img events
2822         /**
2823          * @event click
2824          * The img click event for the img.
2825          * @param {Roo.EventObject} e
2826          */
2827         "click" : true
2828     });
2829 };
2830
2831 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
2832     
2833     imgResponsive: true,
2834     border: '',
2835     src: 'about:blank',
2836     href: false,
2837     target: false,
2838     xsUrl: '',
2839     smUrl: '',
2840     mdUrl: '',
2841     lgUrl: '',
2842
2843     getAutoCreate : function()
2844     {   
2845         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2846             return this.createSingleImg();
2847         }
2848         
2849         var cfg = {
2850             tag: 'div',
2851             cls: 'roo-image-responsive-group',
2852             cn: []
2853         };
2854         var _this = this;
2855         
2856         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2857             
2858             if(!_this[size + 'Url']){
2859                 return;
2860             }
2861             
2862             var img = {
2863                 tag: 'img',
2864                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2865                 html: _this.html || cfg.html,
2866                 src: _this[size + 'Url']
2867             };
2868             
2869             img.cls += ' roo-image-responsive-' + size;
2870             
2871             var s = ['xs', 'sm', 'md', 'lg'];
2872             
2873             s.splice(s.indexOf(size), 1);
2874             
2875             Roo.each(s, function(ss){
2876                 img.cls += ' hidden-' + ss;
2877             });
2878             
2879             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2880                 cfg.cls += ' img-' + _this.border;
2881             }
2882             
2883             if(_this.alt){
2884                 cfg.alt = _this.alt;
2885             }
2886             
2887             if(_this.href){
2888                 var a = {
2889                     tag: 'a',
2890                     href: _this.href,
2891                     cn: [
2892                         img
2893                     ]
2894                 };
2895
2896                 if(this.target){
2897                     a.target = _this.target;
2898                 }
2899             }
2900             
2901             cfg.cn.push((_this.href) ? a : img);
2902             
2903         });
2904         
2905         return cfg;
2906     },
2907     
2908     createSingleImg : function()
2909     {
2910         var cfg = {
2911             tag: 'img',
2912             cls: (this.imgResponsive) ? 'img-responsive' : '',
2913             html : null,
2914             src : 'about:blank'  // just incase src get's set to undefined?!?
2915         };
2916         
2917         cfg.html = this.html || cfg.html;
2918         
2919         cfg.src = this.src || cfg.src;
2920         
2921         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2922             cfg.cls += ' img-' + this.border;
2923         }
2924         
2925         if(this.alt){
2926             cfg.alt = this.alt;
2927         }
2928         
2929         if(this.href){
2930             var a = {
2931                 tag: 'a',
2932                 href: this.href,
2933                 cn: [
2934                     cfg
2935                 ]
2936             };
2937             
2938             if(this.target){
2939                 a.target = this.target;
2940             }
2941             
2942         }
2943         
2944         return (this.href) ? a : cfg;
2945     },
2946     
2947     initEvents: function() 
2948     {
2949         if(!this.href){
2950             this.el.on('click', this.onClick, this);
2951         }
2952         
2953     },
2954     
2955     onClick : function(e)
2956     {
2957         Roo.log('img onclick');
2958         this.fireEvent('click', this, e);
2959     },
2960     /**
2961      * Sets the url of the image - used to update it
2962      * @param {String} url the url of the image
2963      */
2964     
2965     setSrc : function(url)
2966     {
2967         this.src =  url;
2968         
2969         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2970             this.el.dom.src =  url;
2971             return;
2972         }
2973         
2974         this.el.select('img', true).first().dom.src =  url;
2975     }
2976     
2977     
2978    
2979 });
2980
2981  /*
2982  * - LGPL
2983  *
2984  * image
2985  * 
2986  */
2987
2988
2989 /**
2990  * @class Roo.bootstrap.Link
2991  * @extends Roo.bootstrap.Component
2992  * Bootstrap Link Class
2993  * @cfg {String} alt image alternative text
2994  * @cfg {String} href a tag href
2995  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2996  * @cfg {String} html the content of the link.
2997  * @cfg {String} anchor name for the anchor link
2998  * @cfg {String} fa - favicon
2999
3000  * @cfg {Boolean} preventDefault (true | false) default false
3001
3002  * 
3003  * @constructor
3004  * Create a new Input
3005  * @param {Object} config The config object
3006  */
3007
3008 Roo.bootstrap.Link = function(config){
3009     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3010     
3011     this.addEvents({
3012         // img events
3013         /**
3014          * @event click
3015          * The img click event for the img.
3016          * @param {Roo.EventObject} e
3017          */
3018         "click" : true
3019     });
3020 };
3021
3022 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3023     
3024     href: false,
3025     target: false,
3026     preventDefault: false,
3027     anchor : false,
3028     alt : false,
3029     fa: false,
3030
3031
3032     getAutoCreate : function()
3033     {
3034         var html = this.html || '';
3035         
3036         if (this.fa !== false) {
3037             html = '<i class="fa fa-' + this.fa + '"></i>';
3038         }
3039         var cfg = {
3040             tag: 'a'
3041         };
3042         // anchor's do not require html/href...
3043         if (this.anchor === false) {
3044             cfg.html = html;
3045             cfg.href = this.href || '#';
3046         } else {
3047             cfg.name = this.anchor;
3048             if (this.html !== false || this.fa !== false) {
3049                 cfg.html = html;
3050             }
3051             if (this.href !== false) {
3052                 cfg.href = this.href;
3053             }
3054         }
3055         
3056         if(this.alt !== false){
3057             cfg.alt = this.alt;
3058         }
3059         
3060         
3061         if(this.target !== false) {
3062             cfg.target = this.target;
3063         }
3064         
3065         return cfg;
3066     },
3067     
3068     initEvents: function() {
3069         
3070         if(!this.href || this.preventDefault){
3071             this.el.on('click', this.onClick, this);
3072         }
3073     },
3074     
3075     onClick : function(e)
3076     {
3077         if(this.preventDefault){
3078             e.preventDefault();
3079         }
3080         //Roo.log('img onclick');
3081         this.fireEvent('click', this, e);
3082     }
3083    
3084 });
3085
3086  /*
3087  * - LGPL
3088  *
3089  * header
3090  * 
3091  */
3092
3093 /**
3094  * @class Roo.bootstrap.Header
3095  * @extends Roo.bootstrap.Component
3096  * Bootstrap Header class
3097  * @cfg {String} html content of header
3098  * @cfg {Number} level (1|2|3|4|5|6) default 1
3099  * 
3100  * @constructor
3101  * Create a new Header
3102  * @param {Object} config The config object
3103  */
3104
3105
3106 Roo.bootstrap.Header  = function(config){
3107     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3108 };
3109
3110 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3111     
3112     //href : false,
3113     html : false,
3114     level : 1,
3115     
3116     
3117     
3118     getAutoCreate : function(){
3119         
3120         
3121         
3122         var cfg = {
3123             tag: 'h' + (1 *this.level),
3124             html: this.html || ''
3125         } ;
3126         
3127         return cfg;
3128     }
3129    
3130 });
3131
3132  
3133
3134  /*
3135  * Based on:
3136  * Ext JS Library 1.1.1
3137  * Copyright(c) 2006-2007, Ext JS, LLC.
3138  *
3139  * Originally Released Under LGPL - original licence link has changed is not relivant.
3140  *
3141  * Fork - LGPL
3142  * <script type="text/javascript">
3143  */
3144  
3145 /**
3146  * @class Roo.bootstrap.MenuMgr
3147  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3148  * @singleton
3149  */
3150 Roo.bootstrap.MenuMgr = function(){
3151    var menus, active, groups = {}, attached = false, lastShow = new Date();
3152
3153    // private - called when first menu is created
3154    function init(){
3155        menus = {};
3156        active = new Roo.util.MixedCollection();
3157        Roo.get(document).addKeyListener(27, function(){
3158            if(active.length > 0){
3159                hideAll();
3160            }
3161        });
3162    }
3163
3164    // private
3165    function hideAll(){
3166        if(active && active.length > 0){
3167            var c = active.clone();
3168            c.each(function(m){
3169                m.hide();
3170            });
3171        }
3172    }
3173
3174    // private
3175    function onHide(m){
3176        active.remove(m);
3177        if(active.length < 1){
3178            Roo.get(document).un("mouseup", onMouseDown);
3179             
3180            attached = false;
3181        }
3182    }
3183
3184    // private
3185    function onShow(m){
3186        var last = active.last();
3187        lastShow = new Date();
3188        active.add(m);
3189        if(!attached){
3190           Roo.get(document).on("mouseup", onMouseDown);
3191            
3192            attached = true;
3193        }
3194        if(m.parentMenu){
3195           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3196           m.parentMenu.activeChild = m;
3197        }else if(last && last.isVisible()){
3198           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3199        }
3200    }
3201
3202    // private
3203    function onBeforeHide(m){
3204        if(m.activeChild){
3205            m.activeChild.hide();
3206        }
3207        if(m.autoHideTimer){
3208            clearTimeout(m.autoHideTimer);
3209            delete m.autoHideTimer;
3210        }
3211    }
3212
3213    // private
3214    function onBeforeShow(m){
3215        var pm = m.parentMenu;
3216        if(!pm && !m.allowOtherMenus){
3217            hideAll();
3218        }else if(pm && pm.activeChild && active != m){
3219            pm.activeChild.hide();
3220        }
3221    }
3222
3223    // private this should really trigger on mouseup..
3224    function onMouseDown(e){
3225         Roo.log("on Mouse Up");
3226         
3227         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3228             Roo.log("MenuManager hideAll");
3229             hideAll();
3230             e.stopEvent();
3231         }
3232         
3233         
3234    }
3235
3236    // private
3237    function onBeforeCheck(mi, state){
3238        if(state){
3239            var g = groups[mi.group];
3240            for(var i = 0, l = g.length; i < l; i++){
3241                if(g[i] != mi){
3242                    g[i].setChecked(false);
3243                }
3244            }
3245        }
3246    }
3247
3248    return {
3249
3250        /**
3251         * Hides all menus that are currently visible
3252         */
3253        hideAll : function(){
3254             hideAll();  
3255        },
3256
3257        // private
3258        register : function(menu){
3259            if(!menus){
3260                init();
3261            }
3262            menus[menu.id] = menu;
3263            menu.on("beforehide", onBeforeHide);
3264            menu.on("hide", onHide);
3265            menu.on("beforeshow", onBeforeShow);
3266            menu.on("show", onShow);
3267            var g = menu.group;
3268            if(g && menu.events["checkchange"]){
3269                if(!groups[g]){
3270                    groups[g] = [];
3271                }
3272                groups[g].push(menu);
3273                menu.on("checkchange", onCheck);
3274            }
3275        },
3276
3277         /**
3278          * Returns a {@link Roo.menu.Menu} object
3279          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3280          * be used to generate and return a new Menu instance.
3281          */
3282        get : function(menu){
3283            if(typeof menu == "string"){ // menu id
3284                return menus[menu];
3285            }else if(menu.events){  // menu instance
3286                return menu;
3287            }
3288            /*else if(typeof menu.length == 'number'){ // array of menu items?
3289                return new Roo.bootstrap.Menu({items:menu});
3290            }else{ // otherwise, must be a config
3291                return new Roo.bootstrap.Menu(menu);
3292            }
3293            */
3294            return false;
3295        },
3296
3297        // private
3298        unregister : function(menu){
3299            delete menus[menu.id];
3300            menu.un("beforehide", onBeforeHide);
3301            menu.un("hide", onHide);
3302            menu.un("beforeshow", onBeforeShow);
3303            menu.un("show", onShow);
3304            var g = menu.group;
3305            if(g && menu.events["checkchange"]){
3306                groups[g].remove(menu);
3307                menu.un("checkchange", onCheck);
3308            }
3309        },
3310
3311        // private
3312        registerCheckable : function(menuItem){
3313            var g = menuItem.group;
3314            if(g){
3315                if(!groups[g]){
3316                    groups[g] = [];
3317                }
3318                groups[g].push(menuItem);
3319                menuItem.on("beforecheckchange", onBeforeCheck);
3320            }
3321        },
3322
3323        // private
3324        unregisterCheckable : function(menuItem){
3325            var g = menuItem.group;
3326            if(g){
3327                groups[g].remove(menuItem);
3328                menuItem.un("beforecheckchange", onBeforeCheck);
3329            }
3330        }
3331    };
3332 }();/*
3333  * - LGPL
3334  *
3335  * menu
3336  * 
3337  */
3338
3339 /**
3340  * @class Roo.bootstrap.Menu
3341  * @extends Roo.bootstrap.Component
3342  * Bootstrap Menu class - container for MenuItems
3343  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3344  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3345  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3346  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3347  * 
3348  * @constructor
3349  * Create a new Menu
3350  * @param {Object} config The config object
3351  */
3352
3353
3354 Roo.bootstrap.Menu = function(config){
3355     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3356     if (this.registerMenu && this.type != 'treeview')  {
3357         Roo.bootstrap.MenuMgr.register(this);
3358     }
3359     
3360     
3361     this.addEvents({
3362         /**
3363          * @event beforeshow
3364          * Fires before this menu is displayed (return false to block)
3365          * @param {Roo.menu.Menu} this
3366          */
3367         beforeshow : true,
3368         /**
3369          * @event beforehide
3370          * Fires before this menu is hidden (return false to block)
3371          * @param {Roo.menu.Menu} this
3372          */
3373         beforehide : true,
3374         /**
3375          * @event show
3376          * Fires after this menu is displayed
3377          * @param {Roo.menu.Menu} this
3378          */
3379         show : true,
3380         /**
3381          * @event hide
3382          * Fires after this menu is hidden
3383          * @param {Roo.menu.Menu} this
3384          */
3385         hide : true,
3386         /**
3387          * @event click
3388          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3389          * @param {Roo.menu.Menu} this
3390          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3391          * @param {Roo.EventObject} e
3392          */
3393         click : true,
3394         /**
3395          * @event mouseover
3396          * Fires when the mouse is hovering over this menu
3397          * @param {Roo.menu.Menu} this
3398          * @param {Roo.EventObject} e
3399          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3400          */
3401         mouseover : true,
3402         /**
3403          * @event mouseout
3404          * Fires when the mouse exits this menu
3405          * @param {Roo.menu.Menu} this
3406          * @param {Roo.EventObject} e
3407          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3408          */
3409         mouseout : true,
3410         /**
3411          * @event itemclick
3412          * Fires when a menu item contained in this menu is clicked
3413          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3414          * @param {Roo.EventObject} e
3415          */
3416         itemclick: true
3417     });
3418     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3419 };
3420
3421 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3422     
3423    /// html : false,
3424     //align : '',
3425     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3426     type: false,
3427     /**
3428      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3429      */
3430     registerMenu : true,
3431     
3432     menuItems :false, // stores the menu items..
3433     
3434     hidden:true,
3435         
3436     parentMenu : false,
3437     
3438     stopEvent : true,
3439     
3440     isLink : false,
3441     
3442     getChildContainer : function() {
3443         return this.el;  
3444     },
3445     
3446     getAutoCreate : function(){
3447          
3448         //if (['right'].indexOf(this.align)!==-1) {
3449         //    cfg.cn[1].cls += ' pull-right'
3450         //}
3451         
3452         
3453         var cfg = {
3454             tag : 'ul',
3455             cls : 'dropdown-menu' ,
3456             style : 'z-index:1000'
3457             
3458         };
3459         
3460         if (this.type === 'submenu') {
3461             cfg.cls = 'submenu active';
3462         }
3463         if (this.type === 'treeview') {
3464             cfg.cls = 'treeview-menu';
3465         }
3466         
3467         return cfg;
3468     },
3469     initEvents : function() {
3470         
3471        // Roo.log("ADD event");
3472        // Roo.log(this.triggerEl.dom);
3473         
3474         this.triggerEl.on('click', this.onTriggerClick, this);
3475         
3476         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3477         
3478         
3479         if (this.triggerEl.hasClass('nav-item')) {
3480             // dropdown toggle on the 'a' in BS4?
3481             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3482         } else {
3483             this.triggerEl.addClass('dropdown-toggle');
3484         }
3485         if (Roo.isTouch) {
3486             this.el.on('touchstart'  , this.onTouch, this);
3487         }
3488         this.el.on('click' , this.onClick, this);
3489
3490         this.el.on("mouseover", this.onMouseOver, this);
3491         this.el.on("mouseout", this.onMouseOut, this);
3492         
3493     },
3494     
3495     findTargetItem : function(e)
3496     {
3497         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3498         if(!t){
3499             return false;
3500         }
3501         //Roo.log(t);         Roo.log(t.id);
3502         if(t && t.id){
3503             //Roo.log(this.menuitems);
3504             return this.menuitems.get(t.id);
3505             
3506             //return this.items.get(t.menuItemId);
3507         }
3508         
3509         return false;
3510     },
3511     
3512     onTouch : function(e) 
3513     {
3514         Roo.log("menu.onTouch");
3515         //e.stopEvent(); this make the user popdown broken
3516         this.onClick(e);
3517     },
3518     
3519     onClick : function(e)
3520     {
3521         Roo.log("menu.onClick");
3522         
3523         var t = this.findTargetItem(e);
3524         if(!t || t.isContainer){
3525             return;
3526         }
3527         Roo.log(e);
3528         /*
3529         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3530             if(t == this.activeItem && t.shouldDeactivate(e)){
3531                 this.activeItem.deactivate();
3532                 delete this.activeItem;
3533                 return;
3534             }
3535             if(t.canActivate){
3536                 this.setActiveItem(t, true);
3537             }
3538             return;
3539             
3540             
3541         }
3542         */
3543        
3544         Roo.log('pass click event');
3545         
3546         t.onClick(e);
3547         
3548         this.fireEvent("click", this, t, e);
3549         
3550         var _this = this;
3551         
3552         if(!t.href.length || t.href == '#'){
3553             (function() { _this.hide(); }).defer(100);
3554         }
3555         
3556     },
3557     
3558     onMouseOver : function(e){
3559         var t  = this.findTargetItem(e);
3560         //Roo.log(t);
3561         //if(t){
3562         //    if(t.canActivate && !t.disabled){
3563         //        this.setActiveItem(t, true);
3564         //    }
3565         //}
3566         
3567         this.fireEvent("mouseover", this, e, t);
3568     },
3569     isVisible : function(){
3570         return !this.hidden;
3571     },
3572     onMouseOut : function(e){
3573         var t  = this.findTargetItem(e);
3574         
3575         //if(t ){
3576         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3577         //        this.activeItem.deactivate();
3578         //        delete this.activeItem;
3579         //    }
3580         //}
3581         this.fireEvent("mouseout", this, e, t);
3582     },
3583     
3584     
3585     /**
3586      * Displays this menu relative to another element
3587      * @param {String/HTMLElement/Roo.Element} element The element to align to
3588      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3589      * the element (defaults to this.defaultAlign)
3590      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3591      */
3592     show : function(el, pos, parentMenu)
3593     {
3594         if (false === this.fireEvent("beforeshow", this)) {
3595             Roo.log("show canceled");
3596             return;
3597         }
3598         this.parentMenu = parentMenu;
3599         if(!this.el){
3600             this.render();
3601         }
3602         
3603         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3604     },
3605      /**
3606      * Displays this menu at a specific xy position
3607      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3608      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3609      */
3610     showAt : function(xy, parentMenu, /* private: */_e){
3611         this.parentMenu = parentMenu;
3612         if(!this.el){
3613             this.render();
3614         }
3615         if(_e !== false){
3616             this.fireEvent("beforeshow", this);
3617             //xy = this.el.adjustForConstraints(xy);
3618         }
3619         
3620         //this.el.show();
3621         this.hideMenuItems();
3622         this.hidden = false;
3623         this.triggerEl.addClass('open');
3624         this.el.addClass('show');
3625         
3626         // reassign x when hitting right
3627         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3628             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3629         }
3630         
3631         // reassign y when hitting bottom
3632         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3633             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3634         }
3635         
3636         // but the list may align on trigger left or trigger top... should it be a properity?
3637         
3638         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3639             this.el.setXY(xy);
3640         }
3641         
3642         this.focus();
3643         this.fireEvent("show", this);
3644     },
3645     
3646     focus : function(){
3647         return;
3648         if(!this.hidden){
3649             this.doFocus.defer(50, this);
3650         }
3651     },
3652
3653     doFocus : function(){
3654         if(!this.hidden){
3655             this.focusEl.focus();
3656         }
3657     },
3658
3659     /**
3660      * Hides this menu and optionally all parent menus
3661      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3662      */
3663     hide : function(deep)
3664     {
3665         if (false === this.fireEvent("beforehide", this)) {
3666             Roo.log("hide canceled");
3667             return;
3668         }
3669         this.hideMenuItems();
3670         if(this.el && this.isVisible()){
3671            
3672             if(this.activeItem){
3673                 this.activeItem.deactivate();
3674                 this.activeItem = null;
3675             }
3676             this.triggerEl.removeClass('open');;
3677             this.el.removeClass('show');
3678             this.hidden = true;
3679             this.fireEvent("hide", this);
3680         }
3681         if(deep === true && this.parentMenu){
3682             this.parentMenu.hide(true);
3683         }
3684     },
3685     
3686     onTriggerClick : function(e)
3687     {
3688         Roo.log('trigger click');
3689         
3690         var target = e.getTarget();
3691         
3692         Roo.log(target.nodeName.toLowerCase());
3693         
3694         if(target.nodeName.toLowerCase() === 'i'){
3695             e.preventDefault();
3696         }
3697         
3698     },
3699     
3700     onTriggerPress  : function(e)
3701     {
3702         Roo.log('trigger press');
3703         //Roo.log(e.getTarget());
3704        // Roo.log(this.triggerEl.dom);
3705        
3706         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3707         var pel = Roo.get(e.getTarget());
3708         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3709             Roo.log('is treeview or dropdown?');
3710             return;
3711         }
3712         
3713         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3714             return;
3715         }
3716         
3717         if (this.isVisible()) {
3718             Roo.log('hide');
3719             this.hide();
3720         } else {
3721             Roo.log('show');
3722             this.show(this.triggerEl, '?', false);
3723         }
3724         
3725         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3726             e.stopEvent();
3727         }
3728         
3729     },
3730        
3731     
3732     hideMenuItems : function()
3733     {
3734         Roo.log("hide Menu Items");
3735         if (!this.el) { 
3736             return;
3737         }
3738         
3739         this.el.select('.open',true).each(function(aa) {
3740             
3741             aa.removeClass('open');
3742          
3743         });
3744     },
3745     addxtypeChild : function (tree, cntr) {
3746         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3747           
3748         this.menuitems.add(comp);
3749         return comp;
3750
3751     },
3752     getEl : function()
3753     {
3754         Roo.log(this.el);
3755         return this.el;
3756     },
3757     
3758     clear : function()
3759     {
3760         this.getEl().dom.innerHTML = '';
3761         this.menuitems.clear();
3762     }
3763 });
3764
3765  
3766  /*
3767  * - LGPL
3768  *
3769  * menu item
3770  * 
3771  */
3772
3773
3774 /**
3775  * @class Roo.bootstrap.MenuItem
3776  * @extends Roo.bootstrap.Component
3777  * Bootstrap MenuItem class
3778  * @cfg {String} html the menu label
3779  * @cfg {String} href the link
3780  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3781  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3782  * @cfg {Boolean} active  used on sidebars to highlight active itesm
3783  * @cfg {String} fa favicon to show on left of menu item.
3784  * @cfg {Roo.bootsrap.Menu} menu the child menu.
3785  * 
3786  * 
3787  * @constructor
3788  * Create a new MenuItem
3789  * @param {Object} config The config object
3790  */
3791
3792
3793 Roo.bootstrap.MenuItem = function(config){
3794     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3795     this.addEvents({
3796         // raw events
3797         /**
3798          * @event click
3799          * The raw click event for the entire grid.
3800          * @param {Roo.bootstrap.MenuItem} this
3801          * @param {Roo.EventObject} e
3802          */
3803         "click" : true
3804     });
3805 };
3806
3807 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
3808     
3809     href : false,
3810     html : false,
3811     preventDefault: false,
3812     isContainer : false,
3813     active : false,
3814     fa: false,
3815     
3816     getAutoCreate : function(){
3817         
3818         if(this.isContainer){
3819             return {
3820                 tag: 'li',
3821                 cls: 'dropdown-menu-item '
3822             };
3823         }
3824         var ctag = {
3825             tag: 'span',
3826             html: 'Link'
3827         };
3828         
3829         var anc = {
3830             tag : 'a',
3831             cls : 'dropdown-item',
3832             href : '#',
3833             cn : [  ]
3834         };
3835         
3836         if (this.fa !== false) {
3837             anc.cn.push({
3838                 tag : 'i',
3839                 cls : 'fa fa-' + this.fa
3840             });
3841         }
3842         
3843         anc.cn.push(ctag);
3844         
3845         
3846         var cfg= {
3847             tag: 'li',
3848             cls: 'dropdown-menu-item',
3849             cn: [ anc ]
3850         };
3851         if (this.parent().type == 'treeview') {
3852             cfg.cls = 'treeview-menu';
3853         }
3854         if (this.active) {
3855             cfg.cls += ' active';
3856         }
3857         
3858         
3859         
3860         anc.href = this.href || cfg.cn[0].href ;
3861         ctag.html = this.html || cfg.cn[0].html ;
3862         return cfg;
3863     },
3864     
3865     initEvents: function()
3866     {
3867         if (this.parent().type == 'treeview') {
3868             this.el.select('a').on('click', this.onClick, this);
3869         }
3870         
3871         if (this.menu) {
3872             this.menu.parentType = this.xtype;
3873             this.menu.triggerEl = this.el;
3874             this.menu = this.addxtype(Roo.apply({}, this.menu));
3875         }
3876         
3877     },
3878     onClick : function(e)
3879     {
3880         Roo.log('item on click ');
3881         
3882         if(this.preventDefault){
3883             e.preventDefault();
3884         }
3885         //this.parent().hideMenuItems();
3886         
3887         this.fireEvent('click', this, e);
3888     },
3889     getEl : function()
3890     {
3891         return this.el;
3892     } 
3893 });
3894
3895  
3896
3897  /*
3898  * - LGPL
3899  *
3900  * menu separator
3901  * 
3902  */
3903
3904
3905 /**
3906  * @class Roo.bootstrap.MenuSeparator
3907  * @extends Roo.bootstrap.Component
3908  * Bootstrap MenuSeparator class
3909  * 
3910  * @constructor
3911  * Create a new MenuItem
3912  * @param {Object} config The config object
3913  */
3914
3915
3916 Roo.bootstrap.MenuSeparator = function(config){
3917     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3918 };
3919
3920 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
3921     
3922     getAutoCreate : function(){
3923         var cfg = {
3924             cls: 'divider',
3925             tag : 'li'
3926         };
3927         
3928         return cfg;
3929     }
3930    
3931 });
3932
3933  
3934
3935  
3936 /*
3937 * Licence: LGPL
3938 */
3939
3940 /**
3941  * @class Roo.bootstrap.Modal
3942  * @extends Roo.bootstrap.Component
3943  * Bootstrap Modal class
3944  * @cfg {String} title Title of dialog
3945  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3946  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
3947  * @cfg {Boolean} specificTitle default false
3948  * @cfg {Array} buttons Array of buttons or standard button set..
3949  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3950  * @cfg {Boolean} animate default true
3951  * @cfg {Boolean} allow_close default true
3952  * @cfg {Boolean} fitwindow default false
3953  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
3954  * @cfg {Number} width fixed width - usefull for chrome extension only really.
3955  * @cfg {Number} height fixed height - usefull for chrome extension only really.
3956  * @cfg {String} size (sm|lg|xl) default empty
3957  * @cfg {Number} max_width set the max width of modal
3958  * @cfg {Boolean} editableTitle can the title be edited
3959
3960  *
3961  *
3962  * @constructor
3963  * Create a new Modal Dialog
3964  * @param {Object} config The config object
3965  */
3966
3967 Roo.bootstrap.Modal = function(config){
3968     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3969     this.addEvents({
3970         // raw events
3971         /**
3972          * @event btnclick
3973          * The raw btnclick event for the button
3974          * @param {Roo.EventObject} e
3975          */
3976         "btnclick" : true,
3977         /**
3978          * @event resize
3979          * Fire when dialog resize
3980          * @param {Roo.bootstrap.Modal} this
3981          * @param {Roo.EventObject} e
3982          */
3983         "resize" : true,
3984         /**
3985          * @event titlechanged
3986          * Fire when the editable title has been changed
3987          * @param {Roo.bootstrap.Modal} this
3988          * @param {Roo.EventObject} value
3989          */
3990         "titlechanged" : true 
3991         
3992     });
3993     this.buttons = this.buttons || [];
3994
3995     if (this.tmpl) {
3996         this.tmpl = Roo.factory(this.tmpl);
3997     }
3998
3999 };
4000
4001 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4002
4003     title : 'test dialog',
4004
4005     buttons : false,
4006
4007     // set on load...
4008
4009     html: false,
4010
4011     tmp: false,
4012
4013     specificTitle: false,
4014
4015     buttonPosition: 'right',
4016
4017     allow_close : true,
4018
4019     animate : true,
4020
4021     fitwindow: false,
4022     
4023      // private
4024     dialogEl: false,
4025     bodyEl:  false,
4026     footerEl:  false,
4027     titleEl:  false,
4028     closeEl:  false,
4029
4030     size: '',
4031     
4032     max_width: 0,
4033     
4034     max_height: 0,
4035     
4036     fit_content: false,
4037     editableTitle  : false,
4038
4039     onRender : function(ct, position)
4040     {
4041         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4042
4043         if(!this.el){
4044             var cfg = Roo.apply({},  this.getAutoCreate());
4045             cfg.id = Roo.id();
4046             //if(!cfg.name){
4047             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4048             //}
4049             //if (!cfg.name.length) {
4050             //    delete cfg.name;
4051            // }
4052             if (this.cls) {
4053                 cfg.cls += ' ' + this.cls;
4054             }
4055             if (this.style) {
4056                 cfg.style = this.style;
4057             }
4058             this.el = Roo.get(document.body).createChild(cfg, position);
4059         }
4060         //var type = this.el.dom.type;
4061
4062
4063         if(this.tabIndex !== undefined){
4064             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4065         }
4066
4067         this.dialogEl = this.el.select('.modal-dialog',true).first();
4068         this.bodyEl = this.el.select('.modal-body',true).first();
4069         this.closeEl = this.el.select('.modal-header .close', true).first();
4070         this.headerEl = this.el.select('.modal-header',true).first();
4071         this.titleEl = this.el.select('.modal-title',true).first();
4072         this.footerEl = this.el.select('.modal-footer',true).first();
4073
4074         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4075         
4076         //this.el.addClass("x-dlg-modal");
4077
4078         if (this.buttons.length) {
4079             Roo.each(this.buttons, function(bb) {
4080                 var b = Roo.apply({}, bb);
4081                 b.xns = b.xns || Roo.bootstrap;
4082                 b.xtype = b.xtype || 'Button';
4083                 if (typeof(b.listeners) == 'undefined') {
4084                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4085                 }
4086
4087                 var btn = Roo.factory(b);
4088
4089                 btn.render(this.getButtonContainer());
4090
4091             },this);
4092         }
4093         // render the children.
4094         var nitems = [];
4095
4096         if(typeof(this.items) != 'undefined'){
4097             var items = this.items;
4098             delete this.items;
4099
4100             for(var i =0;i < items.length;i++) {
4101                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4102             }
4103         }
4104
4105         this.items = nitems;
4106
4107         // where are these used - they used to be body/close/footer
4108
4109
4110         this.initEvents();
4111         //this.el.addClass([this.fieldClass, this.cls]);
4112
4113     },
4114
4115     getAutoCreate : function()
4116     {
4117         // we will default to modal-body-overflow - might need to remove or make optional later.
4118         var bdy = {
4119                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4120                 html : this.html || ''
4121         };
4122
4123         var title = {
4124             tag: 'h5',
4125             cls : 'modal-title',
4126             html : this.title
4127         };
4128
4129         if(this.specificTitle){ // WTF is this?
4130             title = this.title;
4131         }
4132
4133         var header = [];
4134         if (this.allow_close && Roo.bootstrap.version == 3) {
4135             header.push({
4136                 tag: 'button',
4137                 cls : 'close',
4138                 html : '&times'
4139             });
4140         }
4141
4142         header.push(title);
4143
4144         if (this.editableTitle) {
4145             header.push({
4146                 cls: 'form-control roo-editable-title d-none',
4147                 tag: 'input',
4148                 type: 'text'
4149             });
4150         }
4151         
4152         if (this.allow_close && Roo.bootstrap.version == 4) {
4153             header.push({
4154                 tag: 'button',
4155                 cls : 'close',
4156                 html : '&times'
4157             });
4158         }
4159         
4160         var size = '';
4161
4162         if(this.size.length){
4163             size = 'modal-' + this.size;
4164         }
4165         
4166         var footer = Roo.bootstrap.version == 3 ?
4167             {
4168                 cls : 'modal-footer',
4169                 cn : [
4170                     {
4171                         tag: 'div',
4172                         cls: 'btn-' + this.buttonPosition
4173                     }
4174                 ]
4175
4176             } :
4177             {  // BS4 uses mr-auto on left buttons....
4178                 cls : 'modal-footer'
4179             };
4180
4181             
4182
4183         
4184         
4185         var modal = {
4186             cls: "modal",
4187              cn : [
4188                 {
4189                     cls: "modal-dialog " + size,
4190                     cn : [
4191                         {
4192                             cls : "modal-content",
4193                             cn : [
4194                                 {
4195                                     cls : 'modal-header',
4196                                     cn : header
4197                                 },
4198                                 bdy,
4199                                 footer
4200                             ]
4201
4202                         }
4203                     ]
4204
4205                 }
4206             ]
4207         };
4208
4209         if(this.animate){
4210             modal.cls += ' fade';
4211         }
4212
4213         return modal;
4214
4215     },
4216     getChildContainer : function() {
4217
4218          return this.bodyEl;
4219
4220     },
4221     getButtonContainer : function() {
4222         
4223          return Roo.bootstrap.version == 4 ?
4224             this.el.select('.modal-footer',true).first()
4225             : this.el.select('.modal-footer div',true).first();
4226
4227     },
4228     initEvents : function()
4229     {
4230         if (this.allow_close) {
4231             this.closeEl.on('click', this.hide, this);
4232         }
4233         Roo.EventManager.onWindowResize(this.resize, this, true);
4234         if (this.editableTitle) {
4235             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4236             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4237             this.headerEditEl.on('keyup', function(e) {
4238                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4239                         this.toggleHeaderInput(false)
4240                     }
4241                 }, this);
4242             this.headerEditEl.on('blur', function(e) {
4243                 this.toggleHeaderInput(false)
4244             },this);
4245         }
4246
4247     },
4248   
4249
4250     resize : function()
4251     {
4252         this.maskEl.setSize(
4253             Roo.lib.Dom.getViewWidth(true),
4254             Roo.lib.Dom.getViewHeight(true)
4255         );
4256         
4257         if (this.fitwindow) {
4258             
4259            this.dialogEl.setStyle( { 'max-width' : '100%' });
4260             this.setSize(
4261                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4262                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4263             );
4264             return;
4265         }
4266         
4267         if(this.max_width !== 0) {
4268             
4269             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4270             
4271             if(this.height) {
4272                 this.setSize(w, this.height);
4273                 return;
4274             }
4275             
4276             if(this.max_height) {
4277                 this.setSize(w,Math.min(
4278                     this.max_height,
4279                     Roo.lib.Dom.getViewportHeight(true) - 60
4280                 ));
4281                 
4282                 return;
4283             }
4284             
4285             if(!this.fit_content) {
4286                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4287                 return;
4288             }
4289             
4290             this.setSize(w, Math.min(
4291                 60 +
4292                 this.headerEl.getHeight() + 
4293                 this.footerEl.getHeight() + 
4294                 this.getChildHeight(this.bodyEl.dom.childNodes),
4295                 Roo.lib.Dom.getViewportHeight(true) - 60)
4296             );
4297         }
4298         
4299     },
4300
4301     setSize : function(w,h)
4302     {
4303         if (!w && !h) {
4304             return;
4305         }
4306         
4307         this.resizeTo(w,h);
4308     },
4309
4310     show : function() {
4311
4312         if (!this.rendered) {
4313             this.render();
4314         }
4315         this.toggleHeaderInput(false);
4316         //this.el.setStyle('display', 'block');
4317         this.el.removeClass('hideing');
4318         this.el.dom.style.display='block';
4319         
4320         Roo.get(document.body).addClass('modal-open');
4321  
4322         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4323             
4324             (function(){
4325                 this.el.addClass('show');
4326                 this.el.addClass('in');
4327             }).defer(50, this);
4328         }else{
4329             this.el.addClass('show');
4330             this.el.addClass('in');
4331         }
4332
4333         // not sure how we can show data in here..
4334         //if (this.tmpl) {
4335         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4336         //}
4337
4338         Roo.get(document.body).addClass("x-body-masked");
4339         
4340         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4341         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4342         this.maskEl.dom.style.display = 'block';
4343         this.maskEl.addClass('show');
4344         
4345         
4346         this.resize();
4347         
4348         this.fireEvent('show', this);
4349
4350         // set zindex here - otherwise it appears to be ignored...
4351         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4352
4353         (function () {
4354             this.items.forEach( function(e) {
4355                 e.layout ? e.layout() : false;
4356
4357             });
4358         }).defer(100,this);
4359
4360     },
4361     hide : function()
4362     {
4363         if(this.fireEvent("beforehide", this) !== false){
4364             
4365             this.maskEl.removeClass('show');
4366             
4367             this.maskEl.dom.style.display = '';
4368             Roo.get(document.body).removeClass("x-body-masked");
4369             this.el.removeClass('in');
4370             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4371
4372             if(this.animate){ // why
4373                 this.el.addClass('hideing');
4374                 this.el.removeClass('show');
4375                 (function(){
4376                     if (!this.el.hasClass('hideing')) {
4377                         return; // it's been shown again...
4378                     }
4379                     
4380                     this.el.dom.style.display='';
4381
4382                     Roo.get(document.body).removeClass('modal-open');
4383                     this.el.removeClass('hideing');
4384                 }).defer(150,this);
4385                 
4386             }else{
4387                 this.el.removeClass('show');
4388                 this.el.dom.style.display='';
4389                 Roo.get(document.body).removeClass('modal-open');
4390
4391             }
4392             this.fireEvent('hide', this);
4393         }
4394     },
4395     isVisible : function()
4396     {
4397         
4398         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4399         
4400     },
4401
4402     addButton : function(str, cb)
4403     {
4404
4405
4406         var b = Roo.apply({}, { html : str } );
4407         b.xns = b.xns || Roo.bootstrap;
4408         b.xtype = b.xtype || 'Button';
4409         if (typeof(b.listeners) == 'undefined') {
4410             b.listeners = { click : cb.createDelegate(this)  };
4411         }
4412
4413         var btn = Roo.factory(b);
4414
4415         btn.render(this.getButtonContainer());
4416
4417         return btn;
4418
4419     },
4420
4421     setDefaultButton : function(btn)
4422     {
4423         //this.el.select('.modal-footer').()
4424     },
4425
4426     resizeTo: function(w,h)
4427     {
4428         this.dialogEl.setWidth(w);
4429         
4430         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4431
4432         this.bodyEl.setHeight(h - diff);
4433         
4434         this.fireEvent('resize', this);
4435     },
4436     
4437     setContentSize  : function(w, h)
4438     {
4439
4440     },
4441     onButtonClick: function(btn,e)
4442     {
4443         //Roo.log([a,b,c]);
4444         this.fireEvent('btnclick', btn.name, e);
4445     },
4446      /**
4447      * Set the title of the Dialog
4448      * @param {String} str new Title
4449      */
4450     setTitle: function(str) {
4451         this.titleEl.dom.innerHTML = str;
4452         this.title = str;
4453     },
4454     /**
4455      * Set the body of the Dialog
4456      * @param {String} str new Title
4457      */
4458     setBody: function(str) {
4459         this.bodyEl.dom.innerHTML = str;
4460     },
4461     /**
4462      * Set the body of the Dialog using the template
4463      * @param {Obj} data - apply this data to the template and replace the body contents.
4464      */
4465     applyBody: function(obj)
4466     {
4467         if (!this.tmpl) {
4468             Roo.log("Error - using apply Body without a template");
4469             //code
4470         }
4471         this.tmpl.overwrite(this.bodyEl, obj);
4472     },
4473     
4474     getChildHeight : function(child_nodes)
4475     {
4476         if(
4477             !child_nodes ||
4478             child_nodes.length == 0
4479         ) {
4480             return 0;
4481         }
4482         
4483         var child_height = 0;
4484         
4485         for(var i = 0; i < child_nodes.length; i++) {
4486             
4487             /*
4488             * for modal with tabs...
4489             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4490                 
4491                 var layout_childs = child_nodes[i].childNodes;
4492                 
4493                 for(var j = 0; j < layout_childs.length; j++) {
4494                     
4495                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4496                         
4497                         var layout_body_childs = layout_childs[j].childNodes;
4498                         
4499                         for(var k = 0; k < layout_body_childs.length; k++) {
4500                             
4501                             if(layout_body_childs[k].classList.contains('navbar')) {
4502                                 child_height += layout_body_childs[k].offsetHeight;
4503                                 continue;
4504                             }
4505                             
4506                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4507                                 
4508                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4509                                 
4510                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4511                                     
4512                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4513                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4514                                         continue;
4515                                     }
4516                                     
4517                                 }
4518                                 
4519                             }
4520                             
4521                         }
4522                     }
4523                 }
4524                 continue;
4525             }
4526             */
4527             
4528             child_height += child_nodes[i].offsetHeight;
4529             // Roo.log(child_nodes[i].offsetHeight);
4530         }
4531         
4532         return child_height;
4533     },
4534     toggleHeaderInput : function(is_edit)
4535     {
4536         if (!this.editableTitle) {
4537             return; // not editable.
4538         }
4539         if (is_edit && this.is_header_editing) {
4540             return; // already editing..
4541         }
4542         if (is_edit) {
4543     
4544             this.headerEditEl.dom.value = this.title;
4545             this.headerEditEl.removeClass('d-none');
4546             this.headerEditEl.dom.focus();
4547             this.titleEl.addClass('d-none');
4548             
4549             this.is_header_editing = true;
4550             return
4551         }
4552         // flip back to not editing.
4553         this.title = this.headerEditEl.dom.value;
4554         this.headerEditEl.addClass('d-none');
4555         this.titleEl.removeClass('d-none');
4556         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4557         this.is_header_editing = false;
4558         this.fireEvent('titlechanged', this, this.title);
4559     
4560             
4561         
4562     }
4563
4564 });
4565
4566
4567 Roo.apply(Roo.bootstrap.Modal,  {
4568     /**
4569          * Button config that displays a single OK button
4570          * @type Object
4571          */
4572         OK :  [{
4573             name : 'ok',
4574             weight : 'primary',
4575             html : 'OK'
4576         }],
4577         /**
4578          * Button config that displays Yes and No buttons
4579          * @type Object
4580          */
4581         YESNO : [
4582             {
4583                 name  : 'no',
4584                 html : 'No'
4585             },
4586             {
4587                 name  :'yes',
4588                 weight : 'primary',
4589                 html : 'Yes'
4590             }
4591         ],
4592
4593         /**
4594          * Button config that displays OK and Cancel buttons
4595          * @type Object
4596          */
4597         OKCANCEL : [
4598             {
4599                name : 'cancel',
4600                 html : 'Cancel'
4601             },
4602             {
4603                 name : 'ok',
4604                 weight : 'primary',
4605                 html : 'OK'
4606             }
4607         ],
4608         /**
4609          * Button config that displays Yes, No and Cancel buttons
4610          * @type Object
4611          */
4612         YESNOCANCEL : [
4613             {
4614                 name : 'yes',
4615                 weight : 'primary',
4616                 html : 'Yes'
4617             },
4618             {
4619                 name : 'no',
4620                 html : 'No'
4621             },
4622             {
4623                 name : 'cancel',
4624                 html : 'Cancel'
4625             }
4626         ],
4627         
4628         zIndex : 10001
4629 });
4630
4631 /*
4632  * - LGPL
4633  *
4634  * messagebox - can be used as a replace
4635  * 
4636  */
4637 /**
4638  * @class Roo.MessageBox
4639  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4640  * Example usage:
4641  *<pre><code>
4642 // Basic alert:
4643 Roo.Msg.alert('Status', 'Changes saved successfully.');
4644
4645 // Prompt for user data:
4646 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4647     if (btn == 'ok'){
4648         // process text value...
4649     }
4650 });
4651
4652 // Show a dialog using config options:
4653 Roo.Msg.show({
4654    title:'Save Changes?',
4655    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4656    buttons: Roo.Msg.YESNOCANCEL,
4657    fn: processResult,
4658    animEl: 'elId'
4659 });
4660 </code></pre>
4661  * @singleton
4662  */
4663 Roo.bootstrap.MessageBox = function(){
4664     var dlg, opt, mask, waitTimer;
4665     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4666     var buttons, activeTextEl, bwidth;
4667
4668     
4669     // private
4670     var handleButton = function(button){
4671         dlg.hide();
4672         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4673     };
4674
4675     // private
4676     var handleHide = function(){
4677         if(opt && opt.cls){
4678             dlg.el.removeClass(opt.cls);
4679         }
4680         //if(waitTimer){
4681         //    Roo.TaskMgr.stop(waitTimer);
4682         //    waitTimer = null;
4683         //}
4684     };
4685
4686     // private
4687     var updateButtons = function(b){
4688         var width = 0;
4689         if(!b){
4690             buttons["ok"].hide();
4691             buttons["cancel"].hide();
4692             buttons["yes"].hide();
4693             buttons["no"].hide();
4694             dlg.footerEl.hide();
4695             
4696             return width;
4697         }
4698         dlg.footerEl.show();
4699         for(var k in buttons){
4700             if(typeof buttons[k] != "function"){
4701                 if(b[k]){
4702                     buttons[k].show();
4703                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4704                     width += buttons[k].el.getWidth()+15;
4705                 }else{
4706                     buttons[k].hide();
4707                 }
4708             }
4709         }
4710         return width;
4711     };
4712
4713     // private
4714     var handleEsc = function(d, k, e){
4715         if(opt && opt.closable !== false){
4716             dlg.hide();
4717         }
4718         if(e){
4719             e.stopEvent();
4720         }
4721     };
4722
4723     return {
4724         /**
4725          * Returns a reference to the underlying {@link Roo.BasicDialog} element
4726          * @return {Roo.BasicDialog} The BasicDialog element
4727          */
4728         getDialog : function(){
4729            if(!dlg){
4730                 dlg = new Roo.bootstrap.Modal( {
4731                     //draggable: true,
4732                     //resizable:false,
4733                     //constraintoviewport:false,
4734                     //fixedcenter:true,
4735                     //collapsible : false,
4736                     //shim:true,
4737                     //modal: true,
4738                 //    width: 'auto',
4739                   //  height:100,
4740                     //buttonAlign:"center",
4741                     closeClick : function(){
4742                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4743                             handleButton("no");
4744                         }else{
4745                             handleButton("cancel");
4746                         }
4747                     }
4748                 });
4749                 dlg.render();
4750                 dlg.on("hide", handleHide);
4751                 mask = dlg.mask;
4752                 //dlg.addKeyListener(27, handleEsc);
4753                 buttons = {};
4754                 this.buttons = buttons;
4755                 var bt = this.buttonText;
4756                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4757                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4758                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4759                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4760                 //Roo.log(buttons);
4761                 bodyEl = dlg.bodyEl.createChild({
4762
4763                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4764                         '<textarea class="roo-mb-textarea"></textarea>' +
4765                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
4766                 });
4767                 msgEl = bodyEl.dom.firstChild;
4768                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4769                 textboxEl.enableDisplayMode();
4770                 textboxEl.addKeyListener([10,13], function(){
4771                     if(dlg.isVisible() && opt && opt.buttons){
4772                         if(opt.buttons.ok){
4773                             handleButton("ok");
4774                         }else if(opt.buttons.yes){
4775                             handleButton("yes");
4776                         }
4777                     }
4778                 });
4779                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4780                 textareaEl.enableDisplayMode();
4781                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4782                 progressEl.enableDisplayMode();
4783                 
4784                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4785                 var pf = progressEl.dom.firstChild;
4786                 if (pf) {
4787                     pp = Roo.get(pf.firstChild);
4788                     pp.setHeight(pf.offsetHeight);
4789                 }
4790                 
4791             }
4792             return dlg;
4793         },
4794
4795         /**
4796          * Updates the message box body text
4797          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4798          * the XHTML-compliant non-breaking space character '&amp;#160;')
4799          * @return {Roo.MessageBox} This message box
4800          */
4801         updateText : function(text)
4802         {
4803             if(!dlg.isVisible() && !opt.width){
4804                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4805                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4806             }
4807             msgEl.innerHTML = text || '&#160;';
4808       
4809             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4810             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4811             var w = Math.max(
4812                     Math.min(opt.width || cw , this.maxWidth), 
4813                     Math.max(opt.minWidth || this.minWidth, bwidth)
4814             );
4815             if(opt.prompt){
4816                 activeTextEl.setWidth(w);
4817             }
4818             if(dlg.isVisible()){
4819                 dlg.fixedcenter = false;
4820             }
4821             // to big, make it scroll. = But as usual stupid IE does not support
4822             // !important..
4823             
4824             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4825                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4826                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4827             } else {
4828                 bodyEl.dom.style.height = '';
4829                 bodyEl.dom.style.overflowY = '';
4830             }
4831             if (cw > w) {
4832                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4833             } else {
4834                 bodyEl.dom.style.overflowX = '';
4835             }
4836             
4837             dlg.setContentSize(w, bodyEl.getHeight());
4838             if(dlg.isVisible()){
4839                 dlg.fixedcenter = true;
4840             }
4841             return this;
4842         },
4843
4844         /**
4845          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
4846          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4847          * @param {Number} value Any number between 0 and 1 (e.g., .5)
4848          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4849          * @return {Roo.MessageBox} This message box
4850          */
4851         updateProgress : function(value, text){
4852             if(text){
4853                 this.updateText(text);
4854             }
4855             
4856             if (pp) { // weird bug on my firefox - for some reason this is not defined
4857                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4858                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4859             }
4860             return this;
4861         },        
4862
4863         /**
4864          * Returns true if the message box is currently displayed
4865          * @return {Boolean} True if the message box is visible, else false
4866          */
4867         isVisible : function(){
4868             return dlg && dlg.isVisible();  
4869         },
4870
4871         /**
4872          * Hides the message box if it is displayed
4873          */
4874         hide : function(){
4875             if(this.isVisible()){
4876                 dlg.hide();
4877             }  
4878         },
4879
4880         /**
4881          * Displays a new message box, or reinitializes an existing message box, based on the config options
4882          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4883          * The following config object properties are supported:
4884          * <pre>
4885 Property    Type             Description
4886 ----------  ---------------  ------------------------------------------------------------------------------------
4887 animEl            String/Element   An id or Element from which the message box should animate as it opens and
4888                                    closes (defaults to undefined)
4889 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4890                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
4891 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
4892                                    progress and wait dialogs will ignore this property and always hide the
4893                                    close button as they can only be closed programmatically.
4894 cls               String           A custom CSS class to apply to the message box element
4895 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
4896                                    displayed (defaults to 75)
4897 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
4898                                    function will be btn (the name of the button that was clicked, if applicable,
4899                                    e.g. "ok"), and text (the value of the active text field, if applicable).
4900                                    Progress and wait dialogs will ignore this option since they do not respond to
4901                                    user actions and can only be closed programmatically, so any required function
4902                                    should be called by the same code after it closes the dialog.
4903 icon              String           A CSS class that provides a background image to be used as an icon for
4904                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4905 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
4906 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
4907 modal             Boolean          False to allow user interaction with the page while the message box is
4908                                    displayed (defaults to true)
4909 msg               String           A string that will replace the existing message box body text (defaults
4910                                    to the XHTML-compliant non-breaking space character '&#160;')
4911 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
4912 progress          Boolean          True to display a progress bar (defaults to false)
4913 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
4914 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
4915 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
4916 title             String           The title text
4917 value             String           The string value to set into the active textbox element if displayed
4918 wait              Boolean          True to display a progress bar (defaults to false)
4919 width             Number           The width of the dialog in pixels
4920 </pre>
4921          *
4922          * Example usage:
4923          * <pre><code>
4924 Roo.Msg.show({
4925    title: 'Address',
4926    msg: 'Please enter your address:',
4927    width: 300,
4928    buttons: Roo.MessageBox.OKCANCEL,
4929    multiline: true,
4930    fn: saveAddress,
4931    animEl: 'addAddressBtn'
4932 });
4933 </code></pre>
4934          * @param {Object} config Configuration options
4935          * @return {Roo.MessageBox} This message box
4936          */
4937         show : function(options)
4938         {
4939             
4940             // this causes nightmares if you show one dialog after another
4941             // especially on callbacks..
4942              
4943             if(this.isVisible()){
4944                 
4945                 this.hide();
4946                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4947                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
4948                 Roo.log("New Dialog Message:" +  options.msg )
4949                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4950                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4951                 
4952             }
4953             var d = this.getDialog();
4954             opt = options;
4955             d.setTitle(opt.title || "&#160;");
4956             d.closeEl.setDisplayed(opt.closable !== false);
4957             activeTextEl = textboxEl;
4958             opt.prompt = opt.prompt || (opt.multiline ? true : false);
4959             if(opt.prompt){
4960                 if(opt.multiline){
4961                     textboxEl.hide();
4962                     textareaEl.show();
4963                     textareaEl.setHeight(typeof opt.multiline == "number" ?
4964                         opt.multiline : this.defaultTextHeight);
4965                     activeTextEl = textareaEl;
4966                 }else{
4967                     textboxEl.show();
4968                     textareaEl.hide();
4969                 }
4970             }else{
4971                 textboxEl.hide();
4972                 textareaEl.hide();
4973             }
4974             progressEl.setDisplayed(opt.progress === true);
4975             if (opt.progress) {
4976                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4977             }
4978             this.updateProgress(0);
4979             activeTextEl.dom.value = opt.value || "";
4980             if(opt.prompt){
4981                 dlg.setDefaultButton(activeTextEl);
4982             }else{
4983                 var bs = opt.buttons;
4984                 var db = null;
4985                 if(bs && bs.ok){
4986                     db = buttons["ok"];
4987                 }else if(bs && bs.yes){
4988                     db = buttons["yes"];
4989                 }
4990                 dlg.setDefaultButton(db);
4991             }
4992             bwidth = updateButtons(opt.buttons);
4993             this.updateText(opt.msg);
4994             if(opt.cls){
4995                 d.el.addClass(opt.cls);
4996             }
4997             d.proxyDrag = opt.proxyDrag === true;
4998             d.modal = opt.modal !== false;
4999             d.mask = opt.modal !== false ? mask : false;
5000             if(!d.isVisible()){
5001                 // force it to the end of the z-index stack so it gets a cursor in FF
5002                 document.body.appendChild(dlg.el.dom);
5003                 d.animateTarget = null;
5004                 d.show(options.animEl);
5005             }
5006             return this;
5007         },
5008
5009         /**
5010          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5011          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5012          * and closing the message box when the process is complete.
5013          * @param {String} title The title bar text
5014          * @param {String} msg The message box body text
5015          * @return {Roo.MessageBox} This message box
5016          */
5017         progress : function(title, msg){
5018             this.show({
5019                 title : title,
5020                 msg : msg,
5021                 buttons: false,
5022                 progress:true,
5023                 closable:false,
5024                 minWidth: this.minProgressWidth,
5025                 modal : true
5026             });
5027             return this;
5028         },
5029
5030         /**
5031          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5032          * If a callback function is passed it will be called after the user clicks the button, and the
5033          * id of the button that was clicked will be passed as the only parameter to the callback
5034          * (could also be the top-right close button).
5035          * @param {String} title The title bar text
5036          * @param {String} msg The message box body text
5037          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5038          * @param {Object} scope (optional) The scope of the callback function
5039          * @return {Roo.MessageBox} This message box
5040          */
5041         alert : function(title, msg, fn, scope)
5042         {
5043             this.show({
5044                 title : title,
5045                 msg : msg,
5046                 buttons: this.OK,
5047                 fn: fn,
5048                 closable : false,
5049                 scope : scope,
5050                 modal : true
5051             });
5052             return this;
5053         },
5054
5055         /**
5056          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5057          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5058          * You are responsible for closing the message box when the process is complete.
5059          * @param {String} msg The message box body text
5060          * @param {String} title (optional) The title bar text
5061          * @return {Roo.MessageBox} This message box
5062          */
5063         wait : function(msg, title){
5064             this.show({
5065                 title : title,
5066                 msg : msg,
5067                 buttons: false,
5068                 closable:false,
5069                 progress:true,
5070                 modal:true,
5071                 width:300,
5072                 wait:true
5073             });
5074             waitTimer = Roo.TaskMgr.start({
5075                 run: function(i){
5076                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5077                 },
5078                 interval: 1000
5079             });
5080             return this;
5081         },
5082
5083         /**
5084          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5085          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5086          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5087          * @param {String} title The title bar text
5088          * @param {String} msg The message box body text
5089          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5090          * @param {Object} scope (optional) The scope of the callback function
5091          * @return {Roo.MessageBox} This message box
5092          */
5093         confirm : function(title, msg, fn, scope){
5094             this.show({
5095                 title : title,
5096                 msg : msg,
5097                 buttons: this.YESNO,
5098                 fn: fn,
5099                 scope : scope,
5100                 modal : true
5101             });
5102             return this;
5103         },
5104
5105         /**
5106          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5107          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5108          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5109          * (could also be the top-right close button) and the text that was entered will be passed as the two
5110          * parameters to the callback.
5111          * @param {String} title The title bar text
5112          * @param {String} msg The message box body text
5113          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5114          * @param {Object} scope (optional) The scope of the callback function
5115          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5116          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5117          * @return {Roo.MessageBox} This message box
5118          */
5119         prompt : function(title, msg, fn, scope, multiline){
5120             this.show({
5121                 title : title,
5122                 msg : msg,
5123                 buttons: this.OKCANCEL,
5124                 fn: fn,
5125                 minWidth:250,
5126                 scope : scope,
5127                 prompt:true,
5128                 multiline: multiline,
5129                 modal : true
5130             });
5131             return this;
5132         },
5133
5134         /**
5135          * Button config that displays a single OK button
5136          * @type Object
5137          */
5138         OK : {ok:true},
5139         /**
5140          * Button config that displays Yes and No buttons
5141          * @type Object
5142          */
5143         YESNO : {yes:true, no:true},
5144         /**
5145          * Button config that displays OK and Cancel buttons
5146          * @type Object
5147          */
5148         OKCANCEL : {ok:true, cancel:true},
5149         /**
5150          * Button config that displays Yes, No and Cancel buttons
5151          * @type Object
5152          */
5153         YESNOCANCEL : {yes:true, no:true, cancel:true},
5154
5155         /**
5156          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5157          * @type Number
5158          */
5159         defaultTextHeight : 75,
5160         /**
5161          * The maximum width in pixels of the message box (defaults to 600)
5162          * @type Number
5163          */
5164         maxWidth : 600,
5165         /**
5166          * The minimum width in pixels of the message box (defaults to 100)
5167          * @type Number
5168          */
5169         minWidth : 100,
5170         /**
5171          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5172          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5173          * @type Number
5174          */
5175         minProgressWidth : 250,
5176         /**
5177          * An object containing the default button text strings that can be overriden for localized language support.
5178          * Supported properties are: ok, cancel, yes and no.
5179          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5180          * @type Object
5181          */
5182         buttonText : {
5183             ok : "OK",
5184             cancel : "Cancel",
5185             yes : "Yes",
5186             no : "No"
5187         }
5188     };
5189 }();
5190
5191 /**
5192  * Shorthand for {@link Roo.MessageBox}
5193  */
5194 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5195 Roo.Msg = Roo.Msg || Roo.MessageBox;
5196 /*
5197  * - LGPL
5198  *
5199  * navbar
5200  * 
5201  */
5202
5203 /**
5204  * @class Roo.bootstrap.Navbar
5205  * @extends Roo.bootstrap.Component
5206  * Bootstrap Navbar class
5207
5208  * @constructor
5209  * Create a new Navbar
5210  * @param {Object} config The config object
5211  */
5212
5213
5214 Roo.bootstrap.Navbar = function(config){
5215     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5216     this.addEvents({
5217         // raw events
5218         /**
5219          * @event beforetoggle
5220          * Fire before toggle the menu
5221          * @param {Roo.EventObject} e
5222          */
5223         "beforetoggle" : true
5224     });
5225 };
5226
5227 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5228     
5229     
5230    
5231     // private
5232     navItems : false,
5233     loadMask : false,
5234     
5235     
5236     getAutoCreate : function(){
5237         
5238         
5239         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5240         
5241     },
5242     
5243     initEvents :function ()
5244     {
5245         //Roo.log(this.el.select('.navbar-toggle',true));
5246         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5247         
5248         var mark = {
5249             tag: "div",
5250             cls:"x-dlg-mask"
5251         };
5252         
5253         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5254         
5255         var size = this.el.getSize();
5256         this.maskEl.setSize(size.width, size.height);
5257         this.maskEl.enableDisplayMode("block");
5258         this.maskEl.hide();
5259         
5260         if(this.loadMask){
5261             this.maskEl.show();
5262         }
5263     },
5264     
5265     
5266     getChildContainer : function()
5267     {
5268         if (this.el && this.el.select('.collapse').getCount()) {
5269             return this.el.select('.collapse',true).first();
5270         }
5271         
5272         return this.el;
5273     },
5274     
5275     mask : function()
5276     {
5277         this.maskEl.show();
5278     },
5279     
5280     unmask : function()
5281     {
5282         this.maskEl.hide();
5283     },
5284     onToggle : function()
5285     {
5286         
5287         if(this.fireEvent('beforetoggle', this) === false){
5288             return;
5289         }
5290         var ce = this.el.select('.navbar-collapse',true).first();
5291       
5292         if (!ce.hasClass('show')) {
5293            this.expand();
5294         } else {
5295             this.collapse();
5296         }
5297         
5298         
5299     
5300     },
5301     /**
5302      * Expand the navbar pulldown 
5303      */
5304     expand : function ()
5305     {
5306        
5307         var ce = this.el.select('.navbar-collapse',true).first();
5308         if (ce.hasClass('collapsing')) {
5309             return;
5310         }
5311         ce.dom.style.height = '';
5312                // show it...
5313         ce.addClass('in'); // old...
5314         ce.removeClass('collapse');
5315         ce.addClass('show');
5316         var h = ce.getHeight();
5317         Roo.log(h);
5318         ce.removeClass('show');
5319         // at this point we should be able to see it..
5320         ce.addClass('collapsing');
5321         
5322         ce.setHeight(0); // resize it ...
5323         ce.on('transitionend', function() {
5324             //Roo.log('done transition');
5325             ce.removeClass('collapsing');
5326             ce.addClass('show');
5327             ce.removeClass('collapse');
5328
5329             ce.dom.style.height = '';
5330         }, this, { single: true} );
5331         ce.setHeight(h);
5332         ce.dom.scrollTop = 0;
5333     },
5334     /**
5335      * Collapse the navbar pulldown 
5336      */
5337     collapse : function()
5338     {
5339          var ce = this.el.select('.navbar-collapse',true).first();
5340        
5341         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5342             // it's collapsed or collapsing..
5343             return;
5344         }
5345         ce.removeClass('in'); // old...
5346         ce.setHeight(ce.getHeight());
5347         ce.removeClass('show');
5348         ce.addClass('collapsing');
5349         
5350         ce.on('transitionend', function() {
5351             ce.dom.style.height = '';
5352             ce.removeClass('collapsing');
5353             ce.addClass('collapse');
5354         }, this, { single: true} );
5355         ce.setHeight(0);
5356     }
5357     
5358     
5359     
5360 });
5361
5362
5363
5364  
5365
5366  /*
5367  * - LGPL
5368  *
5369  * navbar
5370  * 
5371  */
5372
5373 /**
5374  * @class Roo.bootstrap.NavSimplebar
5375  * @extends Roo.bootstrap.Navbar
5376  * Bootstrap Sidebar class
5377  *
5378  * @cfg {Boolean} inverse is inverted color
5379  * 
5380  * @cfg {String} type (nav | pills | tabs)
5381  * @cfg {Boolean} arrangement stacked | justified
5382  * @cfg {String} align (left | right) alignment
5383  * 
5384  * @cfg {Boolean} main (true|false) main nav bar? default false
5385  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5386  * 
5387  * @cfg {String} tag (header|footer|nav|div) default is nav 
5388
5389  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5390  * 
5391  * 
5392  * @constructor
5393  * Create a new Sidebar
5394  * @param {Object} config The config object
5395  */
5396
5397
5398 Roo.bootstrap.NavSimplebar = function(config){
5399     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5400 };
5401
5402 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5403     
5404     inverse: false,
5405     
5406     type: false,
5407     arrangement: '',
5408     align : false,
5409     
5410     weight : 'light',
5411     
5412     main : false,
5413     
5414     
5415     tag : false,
5416     
5417     
5418     getAutoCreate : function(){
5419         
5420         
5421         var cfg = {
5422             tag : this.tag || 'div',
5423             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5424         };
5425         if (['light','white'].indexOf(this.weight) > -1) {
5426             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5427         }
5428         cfg.cls += ' bg-' + this.weight;
5429         
5430         if (this.inverse) {
5431             cfg.cls += ' navbar-inverse';
5432             
5433         }
5434         
5435         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5436         
5437         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5438             return cfg;
5439         }
5440         
5441         
5442     
5443         
5444         cfg.cn = [
5445             {
5446                 cls: 'nav nav-' + this.xtype,
5447                 tag : 'ul'
5448             }
5449         ];
5450         
5451          
5452         this.type = this.type || 'nav';
5453         if (['tabs','pills'].indexOf(this.type) != -1) {
5454             cfg.cn[0].cls += ' nav-' + this.type
5455         
5456         
5457         } else {
5458             if (this.type!=='nav') {
5459                 Roo.log('nav type must be nav/tabs/pills')
5460             }
5461             cfg.cn[0].cls += ' navbar-nav'
5462         }
5463         
5464         
5465         
5466         
5467         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5468             cfg.cn[0].cls += ' nav-' + this.arrangement;
5469         }
5470         
5471         
5472         if (this.align === 'right') {
5473             cfg.cn[0].cls += ' navbar-right';
5474         }
5475         
5476         
5477         
5478         
5479         return cfg;
5480     
5481         
5482     }
5483     
5484     
5485     
5486 });
5487
5488
5489
5490  
5491
5492  
5493        /*
5494  * - LGPL
5495  *
5496  * navbar
5497  * navbar-fixed-top
5498  * navbar-expand-md  fixed-top 
5499  */
5500
5501 /**
5502  * @class Roo.bootstrap.NavHeaderbar
5503  * @extends Roo.bootstrap.NavSimplebar
5504  * Bootstrap Sidebar class
5505  *
5506  * @cfg {String} brand what is brand
5507  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5508  * @cfg {String} brand_href href of the brand
5509  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5510  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5511  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5512  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5513  * 
5514  * @constructor
5515  * Create a new Sidebar
5516  * @param {Object} config The config object
5517  */
5518
5519
5520 Roo.bootstrap.NavHeaderbar = function(config){
5521     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5522       
5523 };
5524
5525 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5526     
5527     position: '',
5528     brand: '',
5529     brand_href: false,
5530     srButton : true,
5531     autohide : false,
5532     desktopCenter : false,
5533    
5534     
5535     getAutoCreate : function(){
5536         
5537         var   cfg = {
5538             tag: this.nav || 'nav',
5539             cls: 'navbar navbar-expand-md',
5540             role: 'navigation',
5541             cn: []
5542         };
5543         
5544         var cn = cfg.cn;
5545         if (this.desktopCenter) {
5546             cn.push({cls : 'container', cn : []});
5547             cn = cn[0].cn;
5548         }
5549         
5550         if(this.srButton){
5551             var btn = {
5552                 tag: 'button',
5553                 type: 'button',
5554                 cls: 'navbar-toggle navbar-toggler',
5555                 'data-toggle': 'collapse',
5556                 cn: [
5557                     {
5558                         tag: 'span',
5559                         cls: 'sr-only',
5560                         html: 'Toggle navigation'
5561                     },
5562                     {
5563                         tag: 'span',
5564                         cls: 'icon-bar navbar-toggler-icon'
5565                     },
5566                     {
5567                         tag: 'span',
5568                         cls: 'icon-bar'
5569                     },
5570                     {
5571                         tag: 'span',
5572                         cls: 'icon-bar'
5573                     }
5574                 ]
5575             };
5576             
5577             cn.push( Roo.bootstrap.version == 4 ? btn : {
5578                 tag: 'div',
5579                 cls: 'navbar-header',
5580                 cn: [
5581                     btn
5582                 ]
5583             });
5584         }
5585         
5586         cn.push({
5587             tag: 'div',
5588             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5589             cn : []
5590         });
5591         
5592         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5593         
5594         if (['light','white'].indexOf(this.weight) > -1) {
5595             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5596         }
5597         cfg.cls += ' bg-' + this.weight;
5598         
5599         
5600         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5601             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5602             
5603             // tag can override this..
5604             
5605             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5606         }
5607         
5608         if (this.brand !== '') {
5609             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5610             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5611                 tag: 'a',
5612                 href: this.brand_href ? this.brand_href : '#',
5613                 cls: 'navbar-brand',
5614                 cn: [
5615                 this.brand
5616                 ]
5617             });
5618         }
5619         
5620         if(this.main){
5621             cfg.cls += ' main-nav';
5622         }
5623         
5624         
5625         return cfg;
5626
5627         
5628     },
5629     getHeaderChildContainer : function()
5630     {
5631         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5632             return this.el.select('.navbar-header',true).first();
5633         }
5634         
5635         return this.getChildContainer();
5636     },
5637     
5638     getChildContainer : function()
5639     {
5640          
5641         return this.el.select('.roo-navbar-collapse',true).first();
5642          
5643         
5644     },
5645     
5646     initEvents : function()
5647     {
5648         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5649         
5650         if (this.autohide) {
5651             
5652             var prevScroll = 0;
5653             var ft = this.el;
5654             
5655             Roo.get(document).on('scroll',function(e) {
5656                 var ns = Roo.get(document).getScroll().top;
5657                 var os = prevScroll;
5658                 prevScroll = ns;
5659                 
5660                 if(ns > os){
5661                     ft.removeClass('slideDown');
5662                     ft.addClass('slideUp');
5663                     return;
5664                 }
5665                 ft.removeClass('slideUp');
5666                 ft.addClass('slideDown');
5667                  
5668               
5669           },this);
5670         }
5671     }    
5672     
5673 });
5674
5675
5676
5677  
5678
5679  /*
5680  * - LGPL
5681  *
5682  * navbar
5683  * 
5684  */
5685
5686 /**
5687  * @class Roo.bootstrap.NavSidebar
5688  * @extends Roo.bootstrap.Navbar
5689  * Bootstrap Sidebar class
5690  * 
5691  * @constructor
5692  * Create a new Sidebar
5693  * @param {Object} config The config object
5694  */
5695
5696
5697 Roo.bootstrap.NavSidebar = function(config){
5698     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5699 };
5700
5701 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5702     
5703     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5704     
5705     getAutoCreate : function(){
5706         
5707         
5708         return  {
5709             tag: 'div',
5710             cls: 'sidebar sidebar-nav'
5711         };
5712     
5713         
5714     }
5715     
5716     
5717     
5718 });
5719
5720
5721
5722  
5723
5724  /*
5725  * - LGPL
5726  *
5727  * nav group
5728  * 
5729  */
5730
5731 /**
5732  * @class Roo.bootstrap.NavGroup
5733  * @extends Roo.bootstrap.Component
5734  * Bootstrap NavGroup class
5735  * @cfg {String} align (left|right)
5736  * @cfg {Boolean} inverse
5737  * @cfg {String} type (nav|pills|tab) default nav
5738  * @cfg {String} navId - reference Id for navbar.
5739  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
5740  * 
5741  * @constructor
5742  * Create a new nav group
5743  * @param {Object} config The config object
5744  */
5745
5746 Roo.bootstrap.NavGroup = function(config){
5747     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5748     this.navItems = [];
5749    
5750     Roo.bootstrap.NavGroup.register(this);
5751      this.addEvents({
5752         /**
5753              * @event changed
5754              * Fires when the active item changes
5755              * @param {Roo.bootstrap.NavGroup} this
5756              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5757              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
5758          */
5759         'changed': true
5760      });
5761     
5762 };
5763
5764 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
5765     
5766     align: '',
5767     inverse: false,
5768     form: false,
5769     type: 'nav',
5770     navId : '',
5771     // private
5772     pilltype : true,
5773     
5774     navItems : false, 
5775     
5776     getAutoCreate : function()
5777     {
5778         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5779         
5780         cfg = {
5781             tag : 'ul',
5782             cls: 'nav' 
5783         };
5784         if (Roo.bootstrap.version == 4) {
5785             if (['tabs','pills'].indexOf(this.type) != -1) {
5786                 cfg.cls += ' nav-' + this.type; 
5787             } else {
5788                 // trying to remove so header bar can right align top?
5789                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5790                     // do not use on header bar... 
5791                     cfg.cls += ' navbar-nav';
5792                 }
5793             }
5794             
5795         } else {
5796             if (['tabs','pills'].indexOf(this.type) != -1) {
5797                 cfg.cls += ' nav-' + this.type
5798             } else {
5799                 if (this.type !== 'nav') {
5800                     Roo.log('nav type must be nav/tabs/pills')
5801                 }
5802                 cfg.cls += ' navbar-nav'
5803             }
5804         }
5805         
5806         if (this.parent() && this.parent().sidebar) {
5807             cfg = {
5808                 tag: 'ul',
5809                 cls: 'dashboard-menu sidebar-menu'
5810             };
5811             
5812             return cfg;
5813         }
5814         
5815         if (this.form === true) {
5816             cfg = {
5817                 tag: 'form',
5818                 cls: 'navbar-form form-inline'
5819             };
5820             //nav navbar-right ml-md-auto
5821             if (this.align === 'right') {
5822                 cfg.cls += ' navbar-right ml-md-auto';
5823             } else {
5824                 cfg.cls += ' navbar-left';
5825             }
5826         }
5827         
5828         if (this.align === 'right') {
5829             cfg.cls += ' navbar-right ml-md-auto';
5830         } else {
5831             cfg.cls += ' mr-auto';
5832         }
5833         
5834         if (this.inverse) {
5835             cfg.cls += ' navbar-inverse';
5836             
5837         }
5838         
5839         
5840         return cfg;
5841     },
5842     /**
5843     * sets the active Navigation item
5844     * @param {Roo.bootstrap.NavItem} the new current navitem
5845     */
5846     setActiveItem : function(item)
5847     {
5848         var prev = false;
5849         Roo.each(this.navItems, function(v){
5850             if (v == item) {
5851                 return ;
5852             }
5853             if (v.isActive()) {
5854                 v.setActive(false, true);
5855                 prev = v;
5856                 
5857             }
5858             
5859         });
5860
5861         item.setActive(true, true);
5862         this.fireEvent('changed', this, item, prev);
5863         
5864         
5865     },
5866     /**
5867     * gets the active Navigation item
5868     * @return {Roo.bootstrap.NavItem} the current navitem
5869     */
5870     getActive : function()
5871     {
5872         
5873         var prev = false;
5874         Roo.each(this.navItems, function(v){
5875             
5876             if (v.isActive()) {
5877                 prev = v;
5878                 
5879             }
5880             
5881         });
5882         return prev;
5883     },
5884     
5885     indexOfNav : function()
5886     {
5887         
5888         var prev = false;
5889         Roo.each(this.navItems, function(v,i){
5890             
5891             if (v.isActive()) {
5892                 prev = i;
5893                 
5894             }
5895             
5896         });
5897         return prev;
5898     },
5899     /**
5900     * adds a Navigation item
5901     * @param {Roo.bootstrap.NavItem} the navitem to add
5902     */
5903     addItem : function(cfg)
5904     {
5905         if (this.form && Roo.bootstrap.version == 4) {
5906             cfg.tag = 'div';
5907         }
5908         var cn = new Roo.bootstrap.NavItem(cfg);
5909         this.register(cn);
5910         cn.parentId = this.id;
5911         cn.onRender(this.el, null);
5912         return cn;
5913     },
5914     /**
5915     * register a Navigation item
5916     * @param {Roo.bootstrap.NavItem} the navitem to add
5917     */
5918     register : function(item)
5919     {
5920         this.navItems.push( item);
5921         item.navId = this.navId;
5922     
5923     },
5924     
5925     /**
5926     * clear all the Navigation item
5927     */
5928    
5929     clearAll : function()
5930     {
5931         this.navItems = [];
5932         this.el.dom.innerHTML = '';
5933     },
5934     
5935     getNavItem: function(tabId)
5936     {
5937         var ret = false;
5938         Roo.each(this.navItems, function(e) {
5939             if (e.tabId == tabId) {
5940                ret =  e;
5941                return false;
5942             }
5943             return true;
5944             
5945         });
5946         return ret;
5947     },
5948     
5949     setActiveNext : function()
5950     {
5951         var i = this.indexOfNav(this.getActive());
5952         if (i > this.navItems.length) {
5953             return;
5954         }
5955         this.setActiveItem(this.navItems[i+1]);
5956     },
5957     setActivePrev : function()
5958     {
5959         var i = this.indexOfNav(this.getActive());
5960         if (i  < 1) {
5961             return;
5962         }
5963         this.setActiveItem(this.navItems[i-1]);
5964     },
5965     clearWasActive : function(except) {
5966         Roo.each(this.navItems, function(e) {
5967             if (e.tabId != except.tabId && e.was_active) {
5968                e.was_active = false;
5969                return false;
5970             }
5971             return true;
5972             
5973         });
5974     },
5975     getWasActive : function ()
5976     {
5977         var r = false;
5978         Roo.each(this.navItems, function(e) {
5979             if (e.was_active) {
5980                r = e;
5981                return false;
5982             }
5983             return true;
5984             
5985         });
5986         return r;
5987     }
5988     
5989     
5990 });
5991
5992  
5993 Roo.apply(Roo.bootstrap.NavGroup, {
5994     
5995     groups: {},
5996      /**
5997     * register a Navigation Group
5998     * @param {Roo.bootstrap.NavGroup} the navgroup to add
5999     */
6000     register : function(navgrp)
6001     {
6002         this.groups[navgrp.navId] = navgrp;
6003         
6004     },
6005     /**
6006     * fetch a Navigation Group based on the navigation ID
6007     * @param {string} the navgroup to add
6008     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6009     */
6010     get: function(navId) {
6011         if (typeof(this.groups[navId]) == 'undefined') {
6012             return false;
6013             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6014         }
6015         return this.groups[navId] ;
6016     }
6017     
6018     
6019     
6020 });
6021
6022  /*
6023  * - LGPL
6024  *
6025  * row
6026  * 
6027  */
6028
6029 /**
6030  * @class Roo.bootstrap.NavItem
6031  * @extends Roo.bootstrap.Component
6032  * Bootstrap Navbar.NavItem class
6033  * @cfg {String} href  link to
6034  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6035  * @cfg {Boolean} button_outline show and outlined button
6036  * @cfg {String} html content of button
6037  * @cfg {String} badge text inside badge
6038  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6039  * @cfg {String} glyphicon DEPRICATED - use fa
6040  * @cfg {String} icon DEPRICATED - use fa
6041  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6042  * @cfg {Boolean} active Is item active
6043  * @cfg {Boolean} disabled Is item disabled
6044  * @cfg {String} linkcls  Link Class
6045  * @cfg {Boolean} preventDefault (true | false) default false
6046  * @cfg {String} tabId the tab that this item activates.
6047  * @cfg {String} tagtype (a|span) render as a href or span?
6048  * @cfg {Boolean} animateRef (true|false) link to element default false  
6049   
6050  * @constructor
6051  * Create a new Navbar Item
6052  * @param {Object} config The config object
6053  */
6054 Roo.bootstrap.NavItem = function(config){
6055     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6056     this.addEvents({
6057         // raw events
6058         /**
6059          * @event click
6060          * The raw click event for the entire grid.
6061          * @param {Roo.EventObject} e
6062          */
6063         "click" : true,
6064          /**
6065             * @event changed
6066             * Fires when the active item active state changes
6067             * @param {Roo.bootstrap.NavItem} this
6068             * @param {boolean} state the new state
6069              
6070          */
6071         'changed': true,
6072         /**
6073             * @event scrollto
6074             * Fires when scroll to element
6075             * @param {Roo.bootstrap.NavItem} this
6076             * @param {Object} options
6077             * @param {Roo.EventObject} e
6078              
6079          */
6080         'scrollto': true
6081     });
6082    
6083 };
6084
6085 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6086     
6087     href: false,
6088     html: '',
6089     badge: '',
6090     icon: false,
6091     fa : false,
6092     glyphicon: false,
6093     active: false,
6094     preventDefault : false,
6095     tabId : false,
6096     tagtype : 'a',
6097     tag: 'li',
6098     disabled : false,
6099     animateRef : false,
6100     was_active : false,
6101     button_weight : '',
6102     button_outline : false,
6103     linkcls : '',
6104     navLink: false,
6105     
6106     getAutoCreate : function(){
6107          
6108         var cfg = {
6109             tag: this.tag,
6110             cls: 'nav-item'
6111         };
6112         
6113         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6114         
6115         if (this.active) {
6116             cfg.cls +=  ' active' ;
6117         }
6118         if (this.disabled) {
6119             cfg.cls += ' disabled';
6120         }
6121         
6122         // BS4 only?
6123         if (this.button_weight.length) {
6124             cfg.tag = this.href ? 'a' : 'button';
6125             cfg.html = this.html || '';
6126             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6127             if (this.href) {
6128                 cfg.href = this.href;
6129             }
6130             if (this.fa) {
6131                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6132             }
6133             
6134             // menu .. should add dropdown-menu class - so no need for carat..
6135             
6136             if (this.badge !== '') {
6137                  
6138                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6139             }
6140             return cfg;
6141         }
6142         
6143         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6144             cfg.cn = [
6145                 {
6146                     tag: this.tagtype,
6147                     href : this.href || "#",
6148                     html: this.html || ''
6149                 }
6150             ];
6151             if (this.tagtype == 'a') {
6152                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6153         
6154             }
6155             if (this.icon) {
6156                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6157             }
6158             if (this.fa) {
6159                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6160             }
6161             if(this.glyphicon) {
6162                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6163             }
6164             
6165             if (this.menu) {
6166                 
6167                 cfg.cn[0].html += " <span class='caret'></span>";
6168              
6169             }
6170             
6171             if (this.badge !== '') {
6172                  
6173                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6174             }
6175         }
6176         
6177         
6178         
6179         return cfg;
6180     },
6181     onRender : function(ct, position)
6182     {
6183        // Roo.log("Call onRender: " + this.xtype);
6184         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6185             this.tag = 'div';
6186         }
6187         
6188         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6189         this.navLink = this.el.select('.nav-link',true).first();
6190         return ret;
6191     },
6192       
6193     
6194     initEvents: function() 
6195     {
6196         if (typeof (this.menu) != 'undefined') {
6197             this.menu.parentType = this.xtype;
6198             this.menu.triggerEl = this.el;
6199             this.menu = this.addxtype(Roo.apply({}, this.menu));
6200         }
6201         
6202         this.el.on('click', this.onClick, this);
6203         
6204         //if(this.tagtype == 'span'){
6205         //    this.el.select('span',true).on('click', this.onClick, this);
6206         //}
6207        
6208         // at this point parent should be available..
6209         this.parent().register(this);
6210     },
6211     
6212     onClick : function(e)
6213     {
6214         if (e.getTarget('.dropdown-menu-item')) {
6215             // did you click on a menu itemm.... - then don't trigger onclick..
6216             return;
6217         }
6218         
6219         if(
6220                 this.preventDefault || 
6221                 this.href == '#' 
6222         ){
6223             Roo.log("NavItem - prevent Default?");
6224             e.preventDefault();
6225         }
6226         
6227         if (this.disabled) {
6228             return;
6229         }
6230         
6231         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6232         if (tg && tg.transition) {
6233             Roo.log("waiting for the transitionend");
6234             return;
6235         }
6236         
6237         
6238         
6239         //Roo.log("fire event clicked");
6240         if(this.fireEvent('click', this, e) === false){
6241             return;
6242         };
6243         
6244         if(this.tagtype == 'span'){
6245             return;
6246         }
6247         
6248         //Roo.log(this.href);
6249         var ael = this.el.select('a',true).first();
6250         //Roo.log(ael);
6251         
6252         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6253             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6254             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6255                 return; // ignore... - it's a 'hash' to another page.
6256             }
6257             Roo.log("NavItem - prevent Default?");
6258             e.preventDefault();
6259             this.scrollToElement(e);
6260         }
6261         
6262         
6263         var p =  this.parent();
6264    
6265         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6266             if (typeof(p.setActiveItem) !== 'undefined') {
6267                 p.setActiveItem(this);
6268             }
6269         }
6270         
6271         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6272         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6273             // remove the collapsed menu expand...
6274             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6275         }
6276     },
6277     
6278     isActive: function () {
6279         return this.active
6280     },
6281     setActive : function(state, fire, is_was_active)
6282     {
6283         if (this.active && !state && this.navId) {
6284             this.was_active = true;
6285             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6286             if (nv) {
6287                 nv.clearWasActive(this);
6288             }
6289             
6290         }
6291         this.active = state;
6292         
6293         if (!state ) {
6294             this.el.removeClass('active');
6295             this.navLink ? this.navLink.removeClass('active') : false;
6296         } else if (!this.el.hasClass('active')) {
6297             
6298             this.el.addClass('active');
6299             if (Roo.bootstrap.version == 4 && this.navLink ) {
6300                 this.navLink.addClass('active');
6301             }
6302             
6303         }
6304         if (fire) {
6305             this.fireEvent('changed', this, state);
6306         }
6307         
6308         // show a panel if it's registered and related..
6309         
6310         if (!this.navId || !this.tabId || !state || is_was_active) {
6311             return;
6312         }
6313         
6314         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6315         if (!tg) {
6316             return;
6317         }
6318         var pan = tg.getPanelByName(this.tabId);
6319         if (!pan) {
6320             return;
6321         }
6322         // if we can not flip to new panel - go back to old nav highlight..
6323         if (false == tg.showPanel(pan)) {
6324             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6325             if (nv) {
6326                 var onav = nv.getWasActive();
6327                 if (onav) {
6328                     onav.setActive(true, false, true);
6329                 }
6330             }
6331             
6332         }
6333         
6334         
6335         
6336     },
6337      // this should not be here...
6338     setDisabled : function(state)
6339     {
6340         this.disabled = state;
6341         if (!state ) {
6342             this.el.removeClass('disabled');
6343         } else if (!this.el.hasClass('disabled')) {
6344             this.el.addClass('disabled');
6345         }
6346         
6347     },
6348     
6349     /**
6350      * Fetch the element to display the tooltip on.
6351      * @return {Roo.Element} defaults to this.el
6352      */
6353     tooltipEl : function()
6354     {
6355         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6356     },
6357     
6358     scrollToElement : function(e)
6359     {
6360         var c = document.body;
6361         
6362         /*
6363          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6364          */
6365         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6366             c = document.documentElement;
6367         }
6368         
6369         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6370         
6371         if(!target){
6372             return;
6373         }
6374
6375         var o = target.calcOffsetsTo(c);
6376         
6377         var options = {
6378             target : target,
6379             value : o[1]
6380         };
6381         
6382         this.fireEvent('scrollto', this, options, e);
6383         
6384         Roo.get(c).scrollTo('top', options.value, true);
6385         
6386         return;
6387     }
6388 });
6389  
6390
6391  /*
6392  * - LGPL
6393  *
6394  * sidebar item
6395  *
6396  *  li
6397  *    <span> icon </span>
6398  *    <span> text </span>
6399  *    <span>badge </span>
6400  */
6401
6402 /**
6403  * @class Roo.bootstrap.NavSidebarItem
6404  * @extends Roo.bootstrap.NavItem
6405  * Bootstrap Navbar.NavSidebarItem class
6406  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6407  * {Boolean} open is the menu open
6408  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6409  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6410  * {String} buttonSize (sm|md|lg)the extra classes for the button
6411  * {Boolean} showArrow show arrow next to the text (default true)
6412  * @constructor
6413  * Create a new Navbar Button
6414  * @param {Object} config The config object
6415  */
6416 Roo.bootstrap.NavSidebarItem = function(config){
6417     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6418     this.addEvents({
6419         // raw events
6420         /**
6421          * @event click
6422          * The raw click event for the entire grid.
6423          * @param {Roo.EventObject} e
6424          */
6425         "click" : true,
6426          /**
6427             * @event changed
6428             * Fires when the active item active state changes
6429             * @param {Roo.bootstrap.NavSidebarItem} this
6430             * @param {boolean} state the new state
6431              
6432          */
6433         'changed': true
6434     });
6435    
6436 };
6437
6438 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6439     
6440     badgeWeight : 'default',
6441     
6442     open: false,
6443     
6444     buttonView : false,
6445     
6446     buttonWeight : 'default',
6447     
6448     buttonSize : 'md',
6449     
6450     showArrow : true,
6451     
6452     getAutoCreate : function(){
6453         
6454         
6455         var a = {
6456                 tag: 'a',
6457                 href : this.href || '#',
6458                 cls: '',
6459                 html : '',
6460                 cn : []
6461         };
6462         
6463         if(this.buttonView){
6464             a = {
6465                 tag: 'button',
6466                 href : this.href || '#',
6467                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6468                 html : this.html,
6469                 cn : []
6470             };
6471         }
6472         
6473         var cfg = {
6474             tag: 'li',
6475             cls: '',
6476             cn: [ a ]
6477         };
6478         
6479         if (this.active) {
6480             cfg.cls += ' active';
6481         }
6482         
6483         if (this.disabled) {
6484             cfg.cls += ' disabled';
6485         }
6486         if (this.open) {
6487             cfg.cls += ' open x-open';
6488         }
6489         // left icon..
6490         if (this.glyphicon || this.icon) {
6491             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6492             a.cn.push({ tag : 'i', cls : c }) ;
6493         }
6494         
6495         if(!this.buttonView){
6496             var span = {
6497                 tag: 'span',
6498                 html : this.html || ''
6499             };
6500
6501             a.cn.push(span);
6502             
6503         }
6504         
6505         if (this.badge !== '') {
6506             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6507         }
6508         
6509         if (this.menu) {
6510             
6511             if(this.showArrow){
6512                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6513             }
6514             
6515             a.cls += ' dropdown-toggle treeview' ;
6516         }
6517         
6518         return cfg;
6519     },
6520     
6521     initEvents : function()
6522     { 
6523         if (typeof (this.menu) != 'undefined') {
6524             this.menu.parentType = this.xtype;
6525             this.menu.triggerEl = this.el;
6526             this.menu = this.addxtype(Roo.apply({}, this.menu));
6527         }
6528         
6529         this.el.on('click', this.onClick, this);
6530         
6531         if(this.badge !== ''){
6532             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6533         }
6534         
6535     },
6536     
6537     onClick : function(e)
6538     {
6539         if(this.disabled){
6540             e.preventDefault();
6541             return;
6542         }
6543         
6544         if(this.preventDefault){
6545             e.preventDefault();
6546         }
6547         
6548         this.fireEvent('click', this, e);
6549     },
6550     
6551     disable : function()
6552     {
6553         this.setDisabled(true);
6554     },
6555     
6556     enable : function()
6557     {
6558         this.setDisabled(false);
6559     },
6560     
6561     setDisabled : function(state)
6562     {
6563         if(this.disabled == state){
6564             return;
6565         }
6566         
6567         this.disabled = state;
6568         
6569         if (state) {
6570             this.el.addClass('disabled');
6571             return;
6572         }
6573         
6574         this.el.removeClass('disabled');
6575         
6576         return;
6577     },
6578     
6579     setActive : function(state)
6580     {
6581         if(this.active == state){
6582             return;
6583         }
6584         
6585         this.active = state;
6586         
6587         if (state) {
6588             this.el.addClass('active');
6589             return;
6590         }
6591         
6592         this.el.removeClass('active');
6593         
6594         return;
6595     },
6596     
6597     isActive: function () 
6598     {
6599         return this.active;
6600     },
6601     
6602     setBadge : function(str)
6603     {
6604         if(!this.badgeEl){
6605             return;
6606         }
6607         
6608         this.badgeEl.dom.innerHTML = str;
6609     }
6610     
6611    
6612      
6613  
6614 });
6615  
6616
6617  /*
6618  * - LGPL
6619  *
6620  *  Breadcrumb Nav
6621  * 
6622  */
6623 Roo.namespace('Roo.bootstrap.breadcrumb');
6624
6625
6626 /**
6627  * @class Roo.bootstrap.breadcrumb.Nav
6628  * @extends Roo.bootstrap.Component
6629  * Bootstrap Breadcrumb Nav Class
6630  *  
6631  * @children Roo.bootstrap.breadcrumb.Item
6632  * 
6633  * @constructor
6634  * Create a new breadcrumb.Nav
6635  * @param {Object} config The config object
6636  */
6637
6638
6639 Roo.bootstrap.breadcrumb.Nav = function(config){
6640     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6641     
6642     
6643 };
6644
6645 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6646     
6647     getAutoCreate : function()
6648     {
6649
6650         var cfg = {
6651             tag: 'nav',
6652             cn : [
6653                 {
6654                     tag : 'ol',
6655                     cls : 'breadcrumb'
6656                 }
6657             ]
6658             
6659         };
6660           
6661         return cfg;
6662     },
6663     
6664     initEvents: function()
6665     {
6666         this.olEl = this.el.select('ol',true).first();    
6667     },
6668     getChildContainer : function()
6669     {
6670         return this.olEl;  
6671     }
6672     
6673 });
6674
6675  /*
6676  * - LGPL
6677  *
6678  *  Breadcrumb Item
6679  * 
6680  */
6681
6682
6683 /**
6684  * @class Roo.bootstrap.breadcrumb.Nav
6685  * @extends Roo.bootstrap.Component
6686  * Bootstrap Breadcrumb Nav Class
6687  *  
6688  * @children Roo.bootstrap.breadcrumb.Component
6689  * @cfg {String} html the content of the link.
6690  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6691  * @cfg {Boolean} active is it active
6692
6693  * 
6694  * @constructor
6695  * Create a new breadcrumb.Nav
6696  * @param {Object} config The config object
6697  */
6698
6699 Roo.bootstrap.breadcrumb.Item = function(config){
6700     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6701     this.addEvents({
6702         // img events
6703         /**
6704          * @event click
6705          * The img click event for the img.
6706          * @param {Roo.EventObject} e
6707          */
6708         "click" : true
6709     });
6710     
6711 };
6712
6713 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
6714     
6715     href: false,
6716     html : '',
6717     
6718     getAutoCreate : function()
6719     {
6720
6721         var cfg = {
6722             tag: 'li',
6723             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6724         };
6725         if (this.href !== false) {
6726             cfg.cn = [{
6727                 tag : 'a',
6728                 href : this.href,
6729                 html : this.html
6730             }];
6731         } else {
6732             cfg.html = this.html;
6733         }
6734         
6735         return cfg;
6736     },
6737     
6738     initEvents: function()
6739     {
6740         if (this.href) {
6741             this.el.select('a', true).first().on('click',this.onClick, this)
6742         }
6743         
6744     },
6745     onClick : function(e)
6746     {
6747         e.preventDefault();
6748         this.fireEvent('click',this,  e);
6749     }
6750     
6751 });
6752
6753  /*
6754  * - LGPL
6755  *
6756  * row
6757  * 
6758  */
6759
6760 /**
6761  * @class Roo.bootstrap.Row
6762  * @extends Roo.bootstrap.Component
6763  * Bootstrap Row class (contains columns...)
6764  * 
6765  * @constructor
6766  * Create a new Row
6767  * @param {Object} config The config object
6768  */
6769
6770 Roo.bootstrap.Row = function(config){
6771     Roo.bootstrap.Row.superclass.constructor.call(this, config);
6772 };
6773
6774 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
6775     
6776     getAutoCreate : function(){
6777        return {
6778             cls: 'row clearfix'
6779        };
6780     }
6781     
6782     
6783 });
6784
6785  
6786
6787  /*
6788  * - LGPL
6789  *
6790  * pagination
6791  * 
6792  */
6793
6794 /**
6795  * @class Roo.bootstrap.Pagination
6796  * @extends Roo.bootstrap.Component
6797  * Bootstrap Pagination class
6798  * @cfg {String} size xs | sm | md | lg
6799  * @cfg {Boolean} inverse false | true
6800  * 
6801  * @constructor
6802  * Create a new Pagination
6803  * @param {Object} config The config object
6804  */
6805
6806 Roo.bootstrap.Pagination = function(config){
6807     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6808 };
6809
6810 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
6811     
6812     cls: false,
6813     size: false,
6814     inverse: false,
6815     
6816     getAutoCreate : function(){
6817         var cfg = {
6818             tag: 'ul',
6819                 cls: 'pagination'
6820         };
6821         if (this.inverse) {
6822             cfg.cls += ' inverse';
6823         }
6824         if (this.html) {
6825             cfg.html=this.html;
6826         }
6827         if (this.cls) {
6828             cfg.cls += " " + this.cls;
6829         }
6830         return cfg;
6831     }
6832    
6833 });
6834
6835  
6836
6837  /*
6838  * - LGPL
6839  *
6840  * Pagination item
6841  * 
6842  */
6843
6844
6845 /**
6846  * @class Roo.bootstrap.PaginationItem
6847  * @extends Roo.bootstrap.Component
6848  * Bootstrap PaginationItem class
6849  * @cfg {String} html text
6850  * @cfg {String} href the link
6851  * @cfg {Boolean} preventDefault (true | false) default true
6852  * @cfg {Boolean} active (true | false) default false
6853  * @cfg {Boolean} disabled default false
6854  * 
6855  * 
6856  * @constructor
6857  * Create a new PaginationItem
6858  * @param {Object} config The config object
6859  */
6860
6861
6862 Roo.bootstrap.PaginationItem = function(config){
6863     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6864     this.addEvents({
6865         // raw events
6866         /**
6867          * @event click
6868          * The raw click event for the entire grid.
6869          * @param {Roo.EventObject} e
6870          */
6871         "click" : true
6872     });
6873 };
6874
6875 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
6876     
6877     href : false,
6878     html : false,
6879     preventDefault: true,
6880     active : false,
6881     cls : false,
6882     disabled: false,
6883     
6884     getAutoCreate : function(){
6885         var cfg= {
6886             tag: 'li',
6887             cn: [
6888                 {
6889                     tag : 'a',
6890                     href : this.href ? this.href : '#',
6891                     html : this.html ? this.html : ''
6892                 }
6893             ]
6894         };
6895         
6896         if(this.cls){
6897             cfg.cls = this.cls;
6898         }
6899         
6900         if(this.disabled){
6901             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6902         }
6903         
6904         if(this.active){
6905             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6906         }
6907         
6908         return cfg;
6909     },
6910     
6911     initEvents: function() {
6912         
6913         this.el.on('click', this.onClick, this);
6914         
6915     },
6916     onClick : function(e)
6917     {
6918         Roo.log('PaginationItem on click ');
6919         if(this.preventDefault){
6920             e.preventDefault();
6921         }
6922         
6923         if(this.disabled){
6924             return;
6925         }
6926         
6927         this.fireEvent('click', this, e);
6928     }
6929    
6930 });
6931
6932  
6933
6934  /*
6935  * - LGPL
6936  *
6937  * slider
6938  * 
6939  */
6940
6941
6942 /**
6943  * @class Roo.bootstrap.Slider
6944  * @extends Roo.bootstrap.Component
6945  * Bootstrap Slider class
6946  *    
6947  * @constructor
6948  * Create a new Slider
6949  * @param {Object} config The config object
6950  */
6951
6952 Roo.bootstrap.Slider = function(config){
6953     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6954 };
6955
6956 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
6957     
6958     getAutoCreate : function(){
6959         
6960         var cfg = {
6961             tag: 'div',
6962             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6963             cn: [
6964                 {
6965                     tag: 'a',
6966                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
6967                 }
6968             ]
6969         };
6970         
6971         return cfg;
6972     }
6973    
6974 });
6975
6976  /*
6977  * Based on:
6978  * Ext JS Library 1.1.1
6979  * Copyright(c) 2006-2007, Ext JS, LLC.
6980  *
6981  * Originally Released Under LGPL - original licence link has changed is not relivant.
6982  *
6983  * Fork - LGPL
6984  * <script type="text/javascript">
6985  */
6986  
6987
6988 /**
6989  * @class Roo.grid.ColumnModel
6990  * @extends Roo.util.Observable
6991  * This is the default implementation of a ColumnModel used by the Grid. It defines
6992  * the columns in the grid.
6993  * <br>Usage:<br>
6994  <pre><code>
6995  var colModel = new Roo.grid.ColumnModel([
6996         {header: "Ticker", width: 60, sortable: true, locked: true},
6997         {header: "Company Name", width: 150, sortable: true},
6998         {header: "Market Cap.", width: 100, sortable: true},
6999         {header: "$ Sales", width: 100, sortable: true, renderer: money},
7000         {header: "Employees", width: 100, sortable: true, resizable: false}
7001  ]);
7002  </code></pre>
7003  * <p>
7004  
7005  * The config options listed for this class are options which may appear in each
7006  * individual column definition.
7007  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7008  * @constructor
7009  * @param {Object} config An Array of column config objects. See this class's
7010  * config objects for details.
7011 */
7012 Roo.grid.ColumnModel = function(config){
7013         /**
7014      * The config passed into the constructor
7015      */
7016     this.config = config;
7017     this.lookup = {};
7018
7019     // if no id, create one
7020     // if the column does not have a dataIndex mapping,
7021     // map it to the order it is in the config
7022     for(var i = 0, len = config.length; i < len; i++){
7023         var c = config[i];
7024         if(typeof c.dataIndex == "undefined"){
7025             c.dataIndex = i;
7026         }
7027         if(typeof c.renderer == "string"){
7028             c.renderer = Roo.util.Format[c.renderer];
7029         }
7030         if(typeof c.id == "undefined"){
7031             c.id = Roo.id();
7032         }
7033         if(c.editor && c.editor.xtype){
7034             c.editor  = Roo.factory(c.editor, Roo.grid);
7035         }
7036         if(c.editor && c.editor.isFormField){
7037             c.editor = new Roo.grid.GridEditor(c.editor);
7038         }
7039         this.lookup[c.id] = c;
7040     }
7041
7042     /**
7043      * The width of columns which have no width specified (defaults to 100)
7044      * @type Number
7045      */
7046     this.defaultWidth = 100;
7047
7048     /**
7049      * Default sortable of columns which have no sortable specified (defaults to false)
7050      * @type Boolean
7051      */
7052     this.defaultSortable = false;
7053
7054     this.addEvents({
7055         /**
7056              * @event widthchange
7057              * Fires when the width of a column changes.
7058              * @param {ColumnModel} this
7059              * @param {Number} columnIndex The column index
7060              * @param {Number} newWidth The new width
7061              */
7062             "widthchange": true,
7063         /**
7064              * @event headerchange
7065              * Fires when the text of a header changes.
7066              * @param {ColumnModel} this
7067              * @param {Number} columnIndex The column index
7068              * @param {Number} newText The new header text
7069              */
7070             "headerchange": true,
7071         /**
7072              * @event hiddenchange
7073              * Fires when a column is hidden or "unhidden".
7074              * @param {ColumnModel} this
7075              * @param {Number} columnIndex The column index
7076              * @param {Boolean} hidden true if hidden, false otherwise
7077              */
7078             "hiddenchange": true,
7079             /**
7080          * @event columnmoved
7081          * Fires when a column is moved.
7082          * @param {ColumnModel} this
7083          * @param {Number} oldIndex
7084          * @param {Number} newIndex
7085          */
7086         "columnmoved" : true,
7087         /**
7088          * @event columlockchange
7089          * Fires when a column's locked state is changed
7090          * @param {ColumnModel} this
7091          * @param {Number} colIndex
7092          * @param {Boolean} locked true if locked
7093          */
7094         "columnlockchange" : true
7095     });
7096     Roo.grid.ColumnModel.superclass.constructor.call(this);
7097 };
7098 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7099     /**
7100      * @cfg {String} header The header text to display in the Grid view.
7101      */
7102     /**
7103      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7104      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7105      * specified, the column's index is used as an index into the Record's data Array.
7106      */
7107     /**
7108      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7109      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7110      */
7111     /**
7112      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7113      * Defaults to the value of the {@link #defaultSortable} property.
7114      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7115      */
7116     /**
7117      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7118      */
7119     /**
7120      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7121      */
7122     /**
7123      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7124      */
7125     /**
7126      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7127      */
7128     /**
7129      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7130      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7131      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7132      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7133      */
7134        /**
7135      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7136      */
7137     /**
7138      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7139      */
7140     /**
7141      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7142      */
7143     /**
7144      * @cfg {String} cursor (Optional)
7145      */
7146     /**
7147      * @cfg {String} tooltip (Optional)
7148      */
7149     /**
7150      * @cfg {Number} xs (Optional)
7151      */
7152     /**
7153      * @cfg {Number} sm (Optional)
7154      */
7155     /**
7156      * @cfg {Number} md (Optional)
7157      */
7158     /**
7159      * @cfg {Number} lg (Optional)
7160      */
7161     /**
7162      * Returns the id of the column at the specified index.
7163      * @param {Number} index The column index
7164      * @return {String} the id
7165      */
7166     getColumnId : function(index){
7167         return this.config[index].id;
7168     },
7169
7170     /**
7171      * Returns the column for a specified id.
7172      * @param {String} id The column id
7173      * @return {Object} the column
7174      */
7175     getColumnById : function(id){
7176         return this.lookup[id];
7177     },
7178
7179     
7180     /**
7181      * Returns the column for a specified dataIndex.
7182      * @param {String} dataIndex The column dataIndex
7183      * @return {Object|Boolean} the column or false if not found
7184      */
7185     getColumnByDataIndex: function(dataIndex){
7186         var index = this.findColumnIndex(dataIndex);
7187         return index > -1 ? this.config[index] : false;
7188     },
7189     
7190     /**
7191      * Returns the index for a specified column id.
7192      * @param {String} id The column id
7193      * @return {Number} the index, or -1 if not found
7194      */
7195     getIndexById : function(id){
7196         for(var i = 0, len = this.config.length; i < len; i++){
7197             if(this.config[i].id == id){
7198                 return i;
7199             }
7200         }
7201         return -1;
7202     },
7203     
7204     /**
7205      * Returns the index for a specified column dataIndex.
7206      * @param {String} dataIndex The column dataIndex
7207      * @return {Number} the index, or -1 if not found
7208      */
7209     
7210     findColumnIndex : function(dataIndex){
7211         for(var i = 0, len = this.config.length; i < len; i++){
7212             if(this.config[i].dataIndex == dataIndex){
7213                 return i;
7214             }
7215         }
7216         return -1;
7217     },
7218     
7219     
7220     moveColumn : function(oldIndex, newIndex){
7221         var c = this.config[oldIndex];
7222         this.config.splice(oldIndex, 1);
7223         this.config.splice(newIndex, 0, c);
7224         this.dataMap = null;
7225         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7226     },
7227
7228     isLocked : function(colIndex){
7229         return this.config[colIndex].locked === true;
7230     },
7231
7232     setLocked : function(colIndex, value, suppressEvent){
7233         if(this.isLocked(colIndex) == value){
7234             return;
7235         }
7236         this.config[colIndex].locked = value;
7237         if(!suppressEvent){
7238             this.fireEvent("columnlockchange", this, colIndex, value);
7239         }
7240     },
7241
7242     getTotalLockedWidth : function(){
7243         var totalWidth = 0;
7244         for(var i = 0; i < this.config.length; i++){
7245             if(this.isLocked(i) && !this.isHidden(i)){
7246                 this.totalWidth += this.getColumnWidth(i);
7247             }
7248         }
7249         return totalWidth;
7250     },
7251
7252     getLockedCount : function(){
7253         for(var i = 0, len = this.config.length; i < len; i++){
7254             if(!this.isLocked(i)){
7255                 return i;
7256             }
7257         }
7258         
7259         return this.config.length;
7260     },
7261
7262     /**
7263      * Returns the number of columns.
7264      * @return {Number}
7265      */
7266     getColumnCount : function(visibleOnly){
7267         if(visibleOnly === true){
7268             var c = 0;
7269             for(var i = 0, len = this.config.length; i < len; i++){
7270                 if(!this.isHidden(i)){
7271                     c++;
7272                 }
7273             }
7274             return c;
7275         }
7276         return this.config.length;
7277     },
7278
7279     /**
7280      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7281      * @param {Function} fn
7282      * @param {Object} scope (optional)
7283      * @return {Array} result
7284      */
7285     getColumnsBy : function(fn, scope){
7286         var r = [];
7287         for(var i = 0, len = this.config.length; i < len; i++){
7288             var c = this.config[i];
7289             if(fn.call(scope||this, c, i) === true){
7290                 r[r.length] = c;
7291             }
7292         }
7293         return r;
7294     },
7295
7296     /**
7297      * Returns true if the specified column is sortable.
7298      * @param {Number} col The column index
7299      * @return {Boolean}
7300      */
7301     isSortable : function(col){
7302         if(typeof this.config[col].sortable == "undefined"){
7303             return this.defaultSortable;
7304         }
7305         return this.config[col].sortable;
7306     },
7307
7308     /**
7309      * Returns the rendering (formatting) function defined for the column.
7310      * @param {Number} col The column index.
7311      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7312      */
7313     getRenderer : function(col){
7314         if(!this.config[col].renderer){
7315             return Roo.grid.ColumnModel.defaultRenderer;
7316         }
7317         return this.config[col].renderer;
7318     },
7319
7320     /**
7321      * Sets the rendering (formatting) function for a column.
7322      * @param {Number} col The column index
7323      * @param {Function} fn The function to use to process the cell's raw data
7324      * to return HTML markup for the grid view. The render function is called with
7325      * the following parameters:<ul>
7326      * <li>Data value.</li>
7327      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7328      * <li>css A CSS style string to apply to the table cell.</li>
7329      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7330      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7331      * <li>Row index</li>
7332      * <li>Column index</li>
7333      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7334      */
7335     setRenderer : function(col, fn){
7336         this.config[col].renderer = fn;
7337     },
7338
7339     /**
7340      * Returns the width for the specified column.
7341      * @param {Number} col The column index
7342      * @return {Number}
7343      */
7344     getColumnWidth : function(col){
7345         return this.config[col].width * 1 || this.defaultWidth;
7346     },
7347
7348     /**
7349      * Sets the width for a column.
7350      * @param {Number} col The column index
7351      * @param {Number} width The new width
7352      */
7353     setColumnWidth : function(col, width, suppressEvent){
7354         this.config[col].width = width;
7355         this.totalWidth = null;
7356         if(!suppressEvent){
7357              this.fireEvent("widthchange", this, col, width);
7358         }
7359     },
7360
7361     /**
7362      * Returns the total width of all columns.
7363      * @param {Boolean} includeHidden True to include hidden column widths
7364      * @return {Number}
7365      */
7366     getTotalWidth : function(includeHidden){
7367         if(!this.totalWidth){
7368             this.totalWidth = 0;
7369             for(var i = 0, len = this.config.length; i < len; i++){
7370                 if(includeHidden || !this.isHidden(i)){
7371                     this.totalWidth += this.getColumnWidth(i);
7372                 }
7373             }
7374         }
7375         return this.totalWidth;
7376     },
7377
7378     /**
7379      * Returns the header for the specified column.
7380      * @param {Number} col The column index
7381      * @return {String}
7382      */
7383     getColumnHeader : function(col){
7384         return this.config[col].header;
7385     },
7386
7387     /**
7388      * Sets the header for a column.
7389      * @param {Number} col The column index
7390      * @param {String} header The new header
7391      */
7392     setColumnHeader : function(col, header){
7393         this.config[col].header = header;
7394         this.fireEvent("headerchange", this, col, header);
7395     },
7396
7397     /**
7398      * Returns the tooltip for the specified column.
7399      * @param {Number} col The column index
7400      * @return {String}
7401      */
7402     getColumnTooltip : function(col){
7403             return this.config[col].tooltip;
7404     },
7405     /**
7406      * Sets the tooltip for a column.
7407      * @param {Number} col The column index
7408      * @param {String} tooltip The new tooltip
7409      */
7410     setColumnTooltip : function(col, tooltip){
7411             this.config[col].tooltip = tooltip;
7412     },
7413
7414     /**
7415      * Returns the dataIndex for the specified column.
7416      * @param {Number} col The column index
7417      * @return {Number}
7418      */
7419     getDataIndex : function(col){
7420         return this.config[col].dataIndex;
7421     },
7422
7423     /**
7424      * Sets the dataIndex for a column.
7425      * @param {Number} col The column index
7426      * @param {Number} dataIndex The new dataIndex
7427      */
7428     setDataIndex : function(col, dataIndex){
7429         this.config[col].dataIndex = dataIndex;
7430     },
7431
7432     
7433     
7434     /**
7435      * Returns true if the cell is editable.
7436      * @param {Number} colIndex The column index
7437      * @param {Number} rowIndex The row index - this is nto actually used..?
7438      * @return {Boolean}
7439      */
7440     isCellEditable : function(colIndex, rowIndex){
7441         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7442     },
7443
7444     /**
7445      * Returns the editor defined for the cell/column.
7446      * return false or null to disable editing.
7447      * @param {Number} colIndex The column index
7448      * @param {Number} rowIndex The row index
7449      * @return {Object}
7450      */
7451     getCellEditor : function(colIndex, rowIndex){
7452         return this.config[colIndex].editor;
7453     },
7454
7455     /**
7456      * Sets if a column is editable.
7457      * @param {Number} col The column index
7458      * @param {Boolean} editable True if the column is editable
7459      */
7460     setEditable : function(col, editable){
7461         this.config[col].editable = editable;
7462     },
7463
7464
7465     /**
7466      * Returns true if the column is hidden.
7467      * @param {Number} colIndex The column index
7468      * @return {Boolean}
7469      */
7470     isHidden : function(colIndex){
7471         return this.config[colIndex].hidden;
7472     },
7473
7474
7475     /**
7476      * Returns true if the column width cannot be changed
7477      */
7478     isFixed : function(colIndex){
7479         return this.config[colIndex].fixed;
7480     },
7481
7482     /**
7483      * Returns true if the column can be resized
7484      * @return {Boolean}
7485      */
7486     isResizable : function(colIndex){
7487         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7488     },
7489     /**
7490      * Sets if a column is hidden.
7491      * @param {Number} colIndex The column index
7492      * @param {Boolean} hidden True if the column is hidden
7493      */
7494     setHidden : function(colIndex, hidden){
7495         this.config[colIndex].hidden = hidden;
7496         this.totalWidth = null;
7497         this.fireEvent("hiddenchange", this, colIndex, hidden);
7498     },
7499
7500     /**
7501      * Sets the editor for a column.
7502      * @param {Number} col The column index
7503      * @param {Object} editor The editor object
7504      */
7505     setEditor : function(col, editor){
7506         this.config[col].editor = editor;
7507     }
7508 });
7509
7510 Roo.grid.ColumnModel.defaultRenderer = function(value)
7511 {
7512     if(typeof value == "object") {
7513         return value;
7514     }
7515         if(typeof value == "string" && value.length < 1){
7516             return "&#160;";
7517         }
7518     
7519         return String.format("{0}", value);
7520 };
7521
7522 // Alias for backwards compatibility
7523 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7524 /*
7525  * Based on:
7526  * Ext JS Library 1.1.1
7527  * Copyright(c) 2006-2007, Ext JS, LLC.
7528  *
7529  * Originally Released Under LGPL - original licence link has changed is not relivant.
7530  *
7531  * Fork - LGPL
7532  * <script type="text/javascript">
7533  */
7534  
7535 /**
7536  * @class Roo.LoadMask
7537  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7538  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7539  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7540  * element's UpdateManager load indicator and will be destroyed after the initial load.
7541  * @constructor
7542  * Create a new LoadMask
7543  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7544  * @param {Object} config The config object
7545  */
7546 Roo.LoadMask = function(el, config){
7547     this.el = Roo.get(el);
7548     Roo.apply(this, config);
7549     if(this.store){
7550         this.store.on('beforeload', this.onBeforeLoad, this);
7551         this.store.on('load', this.onLoad, this);
7552         this.store.on('loadexception', this.onLoadException, this);
7553         this.removeMask = false;
7554     }else{
7555         var um = this.el.getUpdateManager();
7556         um.showLoadIndicator = false; // disable the default indicator
7557         um.on('beforeupdate', this.onBeforeLoad, this);
7558         um.on('update', this.onLoad, this);
7559         um.on('failure', this.onLoad, this);
7560         this.removeMask = true;
7561     }
7562 };
7563
7564 Roo.LoadMask.prototype = {
7565     /**
7566      * @cfg {Boolean} removeMask
7567      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7568      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7569      */
7570     /**
7571      * @cfg {String} msg
7572      * The text to display in a centered loading message box (defaults to 'Loading...')
7573      */
7574     msg : 'Loading...',
7575     /**
7576      * @cfg {String} msgCls
7577      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7578      */
7579     msgCls : 'x-mask-loading',
7580
7581     /**
7582      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7583      * @type Boolean
7584      */
7585     disabled: false,
7586
7587     /**
7588      * Disables the mask to prevent it from being displayed
7589      */
7590     disable : function(){
7591        this.disabled = true;
7592     },
7593
7594     /**
7595      * Enables the mask so that it can be displayed
7596      */
7597     enable : function(){
7598         this.disabled = false;
7599     },
7600     
7601     onLoadException : function()
7602     {
7603         Roo.log(arguments);
7604         
7605         if (typeof(arguments[3]) != 'undefined') {
7606             Roo.MessageBox.alert("Error loading",arguments[3]);
7607         } 
7608         /*
7609         try {
7610             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7611                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7612             }   
7613         } catch(e) {
7614             
7615         }
7616         */
7617     
7618         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7619     },
7620     // private
7621     onLoad : function()
7622     {
7623         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7624     },
7625
7626     // private
7627     onBeforeLoad : function(){
7628         if(!this.disabled){
7629             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7630         }
7631     },
7632
7633     // private
7634     destroy : function(){
7635         if(this.store){
7636             this.store.un('beforeload', this.onBeforeLoad, this);
7637             this.store.un('load', this.onLoad, this);
7638             this.store.un('loadexception', this.onLoadException, this);
7639         }else{
7640             var um = this.el.getUpdateManager();
7641             um.un('beforeupdate', this.onBeforeLoad, this);
7642             um.un('update', this.onLoad, this);
7643             um.un('failure', this.onLoad, this);
7644         }
7645     }
7646 };/*
7647  * - LGPL
7648  *
7649  * table
7650  * 
7651  */
7652
7653 /**
7654  * @class Roo.bootstrap.Table
7655  * @extends Roo.bootstrap.Component
7656  * Bootstrap Table class
7657  * @cfg {String} cls table class
7658  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7659  * @cfg {String} bgcolor Specifies the background color for a table
7660  * @cfg {Number} border Specifies whether the table cells should have borders or not
7661  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7662  * @cfg {Number} cellspacing Specifies the space between cells
7663  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7664  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7665  * @cfg {String} sortable Specifies that the table should be sortable
7666  * @cfg {String} summary Specifies a summary of the content of a table
7667  * @cfg {Number} width Specifies the width of a table
7668  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7669  * 
7670  * @cfg {boolean} striped Should the rows be alternative striped
7671  * @cfg {boolean} bordered Add borders to the table
7672  * @cfg {boolean} hover Add hover highlighting
7673  * @cfg {boolean} condensed Format condensed
7674  * @cfg {boolean} responsive Format condensed
7675  * @cfg {Boolean} loadMask (true|false) default false
7676  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7677  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7678  * @cfg {Boolean} rowSelection (true|false) default false
7679  * @cfg {Boolean} cellSelection (true|false) default false
7680  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7681  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7682  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7683  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7684  
7685  * 
7686  * @constructor
7687  * Create a new Table
7688  * @param {Object} config The config object
7689  */
7690
7691 Roo.bootstrap.Table = function(config){
7692     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7693     
7694   
7695     
7696     // BC...
7697     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7698     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7699     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7700     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7701     
7702     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7703     if (this.sm) {
7704         this.sm.grid = this;
7705         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7706         this.sm = this.selModel;
7707         this.sm.xmodule = this.xmodule || false;
7708     }
7709     
7710     if (this.cm && typeof(this.cm.config) == 'undefined') {
7711         this.colModel = new Roo.grid.ColumnModel(this.cm);
7712         this.cm = this.colModel;
7713         this.cm.xmodule = this.xmodule || false;
7714     }
7715     if (this.store) {
7716         this.store= Roo.factory(this.store, Roo.data);
7717         this.ds = this.store;
7718         this.ds.xmodule = this.xmodule || false;
7719          
7720     }
7721     if (this.footer && this.store) {
7722         this.footer.dataSource = this.ds;
7723         this.footer = Roo.factory(this.footer);
7724     }
7725     
7726     /** @private */
7727     this.addEvents({
7728         /**
7729          * @event cellclick
7730          * Fires when a cell is clicked
7731          * @param {Roo.bootstrap.Table} this
7732          * @param {Roo.Element} el
7733          * @param {Number} rowIndex
7734          * @param {Number} columnIndex
7735          * @param {Roo.EventObject} e
7736          */
7737         "cellclick" : true,
7738         /**
7739          * @event celldblclick
7740          * Fires when a cell is double clicked
7741          * @param {Roo.bootstrap.Table} this
7742          * @param {Roo.Element} el
7743          * @param {Number} rowIndex
7744          * @param {Number} columnIndex
7745          * @param {Roo.EventObject} e
7746          */
7747         "celldblclick" : true,
7748         /**
7749          * @event rowclick
7750          * Fires when a row is clicked
7751          * @param {Roo.bootstrap.Table} this
7752          * @param {Roo.Element} el
7753          * @param {Number} rowIndex
7754          * @param {Roo.EventObject} e
7755          */
7756         "rowclick" : true,
7757         /**
7758          * @event rowdblclick
7759          * Fires when a row is double clicked
7760          * @param {Roo.bootstrap.Table} this
7761          * @param {Roo.Element} el
7762          * @param {Number} rowIndex
7763          * @param {Roo.EventObject} e
7764          */
7765         "rowdblclick" : true,
7766         /**
7767          * @event mouseover
7768          * Fires when a mouseover occur
7769          * @param {Roo.bootstrap.Table} this
7770          * @param {Roo.Element} el
7771          * @param {Number} rowIndex
7772          * @param {Number} columnIndex
7773          * @param {Roo.EventObject} e
7774          */
7775         "mouseover" : true,
7776         /**
7777          * @event mouseout
7778          * Fires when a mouseout occur
7779          * @param {Roo.bootstrap.Table} this
7780          * @param {Roo.Element} el
7781          * @param {Number} rowIndex
7782          * @param {Number} columnIndex
7783          * @param {Roo.EventObject} e
7784          */
7785         "mouseout" : true,
7786         /**
7787          * @event rowclass
7788          * Fires when a row is rendered, so you can change add a style to it.
7789          * @param {Roo.bootstrap.Table} this
7790          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
7791          */
7792         'rowclass' : true,
7793           /**
7794          * @event rowsrendered
7795          * Fires when all the  rows have been rendered
7796          * @param {Roo.bootstrap.Table} this
7797          */
7798         'rowsrendered' : true,
7799         /**
7800          * @event contextmenu
7801          * The raw contextmenu event for the entire grid.
7802          * @param {Roo.EventObject} e
7803          */
7804         "contextmenu" : true,
7805         /**
7806          * @event rowcontextmenu
7807          * Fires when a row is right clicked
7808          * @param {Roo.bootstrap.Table} this
7809          * @param {Number} rowIndex
7810          * @param {Roo.EventObject} e
7811          */
7812         "rowcontextmenu" : true,
7813         /**
7814          * @event cellcontextmenu
7815          * Fires when a cell is right clicked
7816          * @param {Roo.bootstrap.Table} this
7817          * @param {Number} rowIndex
7818          * @param {Number} cellIndex
7819          * @param {Roo.EventObject} e
7820          */
7821          "cellcontextmenu" : true,
7822          /**
7823          * @event headercontextmenu
7824          * Fires when a header is right clicked
7825          * @param {Roo.bootstrap.Table} this
7826          * @param {Number} columnIndex
7827          * @param {Roo.EventObject} e
7828          */
7829         "headercontextmenu" : true
7830     });
7831 };
7832
7833 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
7834     
7835     cls: false,
7836     align: false,
7837     bgcolor: false,
7838     border: false,
7839     cellpadding: false,
7840     cellspacing: false,
7841     frame: false,
7842     rules: false,
7843     sortable: false,
7844     summary: false,
7845     width: false,
7846     striped : false,
7847     scrollBody : false,
7848     bordered: false,
7849     hover:  false,
7850     condensed : false,
7851     responsive : false,
7852     sm : false,
7853     cm : false,
7854     store : false,
7855     loadMask : false,
7856     footerShow : true,
7857     headerShow : true,
7858   
7859     rowSelection : false,
7860     cellSelection : false,
7861     layout : false,
7862     
7863     // Roo.Element - the tbody
7864     mainBody: false,
7865     // Roo.Element - thead element
7866     mainHead: false,
7867     
7868     container: false, // used by gridpanel...
7869     
7870     lazyLoad : false,
7871     
7872     CSS : Roo.util.CSS,
7873     
7874     auto_hide_footer : false,
7875     
7876     getAutoCreate : function()
7877     {
7878         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7879         
7880         cfg = {
7881             tag: 'table',
7882             cls : 'table',
7883             cn : []
7884         };
7885         if (this.scrollBody) {
7886             cfg.cls += ' table-body-fixed';
7887         }    
7888         if (this.striped) {
7889             cfg.cls += ' table-striped';
7890         }
7891         
7892         if (this.hover) {
7893             cfg.cls += ' table-hover';
7894         }
7895         if (this.bordered) {
7896             cfg.cls += ' table-bordered';
7897         }
7898         if (this.condensed) {
7899             cfg.cls += ' table-condensed';
7900         }
7901         if (this.responsive) {
7902             cfg.cls += ' table-responsive';
7903         }
7904         
7905         if (this.cls) {
7906             cfg.cls+=  ' ' +this.cls;
7907         }
7908         
7909         // this lot should be simplifed...
7910         var _t = this;
7911         var cp = [
7912             'align',
7913             'bgcolor',
7914             'border',
7915             'cellpadding',
7916             'cellspacing',
7917             'frame',
7918             'rules',
7919             'sortable',
7920             'summary',
7921             'width'
7922         ].forEach(function(k) {
7923             if (_t[k]) {
7924                 cfg[k] = _t[k];
7925             }
7926         });
7927         
7928         
7929         if (this.layout) {
7930             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7931         }
7932         
7933         if(this.store || this.cm){
7934             if(this.headerShow){
7935                 cfg.cn.push(this.renderHeader());
7936             }
7937             
7938             cfg.cn.push(this.renderBody());
7939             
7940             if(this.footerShow){
7941                 cfg.cn.push(this.renderFooter());
7942             }
7943             // where does this come from?
7944             //cfg.cls+=  ' TableGrid';
7945         }
7946         
7947         return { cn : [ cfg ] };
7948     },
7949     
7950     initEvents : function()
7951     {   
7952         if(!this.store || !this.cm){
7953             return;
7954         }
7955         if (this.selModel) {
7956             this.selModel.initEvents();
7957         }
7958         
7959         
7960         //Roo.log('initEvents with ds!!!!');
7961         
7962         this.mainBody = this.el.select('tbody', true).first();
7963         this.mainHead = this.el.select('thead', true).first();
7964         this.mainFoot = this.el.select('tfoot', true).first();
7965         
7966         
7967         
7968         var _this = this;
7969         
7970         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7971             e.on('click', _this.sort, _this);
7972         });
7973         
7974         this.mainBody.on("click", this.onClick, this);
7975         this.mainBody.on("dblclick", this.onDblClick, this);
7976         
7977         // why is this done????? = it breaks dialogs??
7978         //this.parent().el.setStyle('position', 'relative');
7979         
7980         
7981         if (this.footer) {
7982             this.footer.parentId = this.id;
7983             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7984             
7985             if(this.lazyLoad){
7986                 this.el.select('tfoot tr td').first().addClass('hide');
7987             }
7988         } 
7989         
7990         if(this.loadMask) {
7991             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7992         }
7993         
7994         this.store.on('load', this.onLoad, this);
7995         this.store.on('beforeload', this.onBeforeLoad, this);
7996         this.store.on('update', this.onUpdate, this);
7997         this.store.on('add', this.onAdd, this);
7998         this.store.on("clear", this.clear, this);
7999         
8000         this.el.on("contextmenu", this.onContextMenu, this);
8001         
8002         this.mainBody.on('scroll', this.onBodyScroll, this);
8003         
8004         this.cm.on("headerchange", this.onHeaderChange, this);
8005         
8006         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8007         
8008     },
8009     
8010     onContextMenu : function(e, t)
8011     {
8012         this.processEvent("contextmenu", e);
8013     },
8014     
8015     processEvent : function(name, e)
8016     {
8017         if (name != 'touchstart' ) {
8018             this.fireEvent(name, e);    
8019         }
8020         
8021         var t = e.getTarget();
8022         
8023         var cell = Roo.get(t);
8024         
8025         if(!cell){
8026             return;
8027         }
8028         
8029         if(cell.findParent('tfoot', false, true)){
8030             return;
8031         }
8032         
8033         if(cell.findParent('thead', false, true)){
8034             
8035             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8036                 cell = Roo.get(t).findParent('th', false, true);
8037                 if (!cell) {
8038                     Roo.log("failed to find th in thead?");
8039                     Roo.log(e.getTarget());
8040                     return;
8041                 }
8042             }
8043             
8044             var cellIndex = cell.dom.cellIndex;
8045             
8046             var ename = name == 'touchstart' ? 'click' : name;
8047             this.fireEvent("header" + ename, this, cellIndex, e);
8048             
8049             return;
8050         }
8051         
8052         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8053             cell = Roo.get(t).findParent('td', false, true);
8054             if (!cell) {
8055                 Roo.log("failed to find th in tbody?");
8056                 Roo.log(e.getTarget());
8057                 return;
8058             }
8059         }
8060         
8061         var row = cell.findParent('tr', false, true);
8062         var cellIndex = cell.dom.cellIndex;
8063         var rowIndex = row.dom.rowIndex - 1;
8064         
8065         if(row !== false){
8066             
8067             this.fireEvent("row" + name, this, rowIndex, e);
8068             
8069             if(cell !== false){
8070             
8071                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8072             }
8073         }
8074         
8075     },
8076     
8077     onMouseover : function(e, el)
8078     {
8079         var cell = Roo.get(el);
8080         
8081         if(!cell){
8082             return;
8083         }
8084         
8085         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8086             cell = cell.findParent('td', false, true);
8087         }
8088         
8089         var row = cell.findParent('tr', false, true);
8090         var cellIndex = cell.dom.cellIndex;
8091         var rowIndex = row.dom.rowIndex - 1; // start from 0
8092         
8093         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8094         
8095     },
8096     
8097     onMouseout : function(e, el)
8098     {
8099         var cell = Roo.get(el);
8100         
8101         if(!cell){
8102             return;
8103         }
8104         
8105         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8106             cell = cell.findParent('td', false, true);
8107         }
8108         
8109         var row = cell.findParent('tr', false, true);
8110         var cellIndex = cell.dom.cellIndex;
8111         var rowIndex = row.dom.rowIndex - 1; // start from 0
8112         
8113         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8114         
8115     },
8116     
8117     onClick : function(e, el)
8118     {
8119         var cell = Roo.get(el);
8120         
8121         if(!cell || (!this.cellSelection && !this.rowSelection)){
8122             return;
8123         }
8124         
8125         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8126             cell = cell.findParent('td', false, true);
8127         }
8128         
8129         if(!cell || typeof(cell) == 'undefined'){
8130             return;
8131         }
8132         
8133         var row = cell.findParent('tr', false, true);
8134         
8135         if(!row || typeof(row) == 'undefined'){
8136             return;
8137         }
8138         
8139         var cellIndex = cell.dom.cellIndex;
8140         var rowIndex = this.getRowIndex(row);
8141         
8142         // why??? - should these not be based on SelectionModel?
8143         if(this.cellSelection){
8144             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8145         }
8146         
8147         if(this.rowSelection){
8148             this.fireEvent('rowclick', this, row, rowIndex, e);
8149         }
8150         
8151         
8152     },
8153         
8154     onDblClick : function(e,el)
8155     {
8156         var cell = Roo.get(el);
8157         
8158         if(!cell || (!this.cellSelection && !this.rowSelection)){
8159             return;
8160         }
8161         
8162         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8163             cell = cell.findParent('td', false, true);
8164         }
8165         
8166         if(!cell || typeof(cell) == 'undefined'){
8167             return;
8168         }
8169         
8170         var row = cell.findParent('tr', false, true);
8171         
8172         if(!row || typeof(row) == 'undefined'){
8173             return;
8174         }
8175         
8176         var cellIndex = cell.dom.cellIndex;
8177         var rowIndex = this.getRowIndex(row);
8178         
8179         if(this.cellSelection){
8180             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8181         }
8182         
8183         if(this.rowSelection){
8184             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8185         }
8186     },
8187     
8188     sort : function(e,el)
8189     {
8190         var col = Roo.get(el);
8191         
8192         if(!col.hasClass('sortable')){
8193             return;
8194         }
8195         
8196         var sort = col.attr('sort');
8197         var dir = 'ASC';
8198         
8199         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8200             dir = 'DESC';
8201         }
8202         
8203         this.store.sortInfo = {field : sort, direction : dir};
8204         
8205         if (this.footer) {
8206             Roo.log("calling footer first");
8207             this.footer.onClick('first');
8208         } else {
8209         
8210             this.store.load({ params : { start : 0 } });
8211         }
8212     },
8213     
8214     renderHeader : function()
8215     {
8216         var header = {
8217             tag: 'thead',
8218             cn : []
8219         };
8220         
8221         var cm = this.cm;
8222         this.totalWidth = 0;
8223         
8224         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8225             
8226             var config = cm.config[i];
8227             
8228             var c = {
8229                 tag: 'th',
8230                 cls : 'x-hcol-' + i,
8231                 style : '',
8232                 html: cm.getColumnHeader(i)
8233             };
8234             
8235             var hh = '';
8236             
8237             if(typeof(config.sortable) != 'undefined' && config.sortable){
8238                 c.cls = 'sortable';
8239                 c.html = '<i class="glyphicon"></i>' + c.html;
8240             }
8241             
8242             // could use BS4 hidden-..-down 
8243             
8244             if(typeof(config.lgHeader) != 'undefined'){
8245                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8246             }
8247             
8248             if(typeof(config.mdHeader) != 'undefined'){
8249                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8250             }
8251             
8252             if(typeof(config.smHeader) != 'undefined'){
8253                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8254             }
8255             
8256             if(typeof(config.xsHeader) != 'undefined'){
8257                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8258             }
8259             
8260             if(hh.length){
8261                 c.html = hh;
8262             }
8263             
8264             if(typeof(config.tooltip) != 'undefined'){
8265                 c.tooltip = config.tooltip;
8266             }
8267             
8268             if(typeof(config.colspan) != 'undefined'){
8269                 c.colspan = config.colspan;
8270             }
8271             
8272             if(typeof(config.hidden) != 'undefined' && config.hidden){
8273                 c.style += ' display:none;';
8274             }
8275             
8276             if(typeof(config.dataIndex) != 'undefined'){
8277                 c.sort = config.dataIndex;
8278             }
8279             
8280            
8281             
8282             if(typeof(config.align) != 'undefined' && config.align.length){
8283                 c.style += ' text-align:' + config.align + ';';
8284             }
8285             
8286             if(typeof(config.width) != 'undefined'){
8287                 c.style += ' width:' + config.width + 'px;';
8288                 this.totalWidth += config.width;
8289             } else {
8290                 this.totalWidth += 100; // assume minimum of 100 per column?
8291             }
8292             
8293             if(typeof(config.cls) != 'undefined'){
8294                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8295             }
8296             
8297             ['xs','sm','md','lg'].map(function(size){
8298                 
8299                 if(typeof(config[size]) == 'undefined'){
8300                     return;
8301                 }
8302                  
8303                 if (!config[size]) { // 0 = hidden
8304                     // BS 4 '0' is treated as hide that column and below.
8305                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8306                     return;
8307                 }
8308                 
8309                 c.cls += ' col-' + size + '-' + config[size] + (
8310                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8311                 );
8312                 
8313                 
8314             });
8315             
8316             header.cn.push(c)
8317         }
8318         
8319         return header;
8320     },
8321     
8322     renderBody : function()
8323     {
8324         var body = {
8325             tag: 'tbody',
8326             cn : [
8327                 {
8328                     tag: 'tr',
8329                     cn : [
8330                         {
8331                             tag : 'td',
8332                             colspan :  this.cm.getColumnCount()
8333                         }
8334                     ]
8335                 }
8336             ]
8337         };
8338         
8339         return body;
8340     },
8341     
8342     renderFooter : function()
8343     {
8344         var footer = {
8345             tag: 'tfoot',
8346             cn : [
8347                 {
8348                     tag: 'tr',
8349                     cn : [
8350                         {
8351                             tag : 'td',
8352                             colspan :  this.cm.getColumnCount()
8353                         }
8354                     ]
8355                 }
8356             ]
8357         };
8358         
8359         return footer;
8360     },
8361     
8362     
8363     
8364     onLoad : function()
8365     {
8366 //        Roo.log('ds onload');
8367         this.clear();
8368         
8369         var _this = this;
8370         var cm = this.cm;
8371         var ds = this.store;
8372         
8373         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8374             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8375             if (_this.store.sortInfo) {
8376                     
8377                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8378                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8379                 }
8380                 
8381                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8382                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8383                 }
8384             }
8385         });
8386         
8387         var tbody =  this.mainBody;
8388               
8389         if(ds.getCount() > 0){
8390             ds.data.each(function(d,rowIndex){
8391                 var row =  this.renderRow(cm, ds, rowIndex);
8392                 
8393                 tbody.createChild(row);
8394                 
8395                 var _this = this;
8396                 
8397                 if(row.cellObjects.length){
8398                     Roo.each(row.cellObjects, function(r){
8399                         _this.renderCellObject(r);
8400                     })
8401                 }
8402                 
8403             }, this);
8404         }
8405         
8406         var tfoot = this.el.select('tfoot', true).first();
8407         
8408         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8409             
8410             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8411             
8412             var total = this.ds.getTotalCount();
8413             
8414             if(this.footer.pageSize < total){
8415                 this.mainFoot.show();
8416             }
8417         }
8418         
8419         Roo.each(this.el.select('tbody td', true).elements, function(e){
8420             e.on('mouseover', _this.onMouseover, _this);
8421         });
8422         
8423         Roo.each(this.el.select('tbody td', true).elements, function(e){
8424             e.on('mouseout', _this.onMouseout, _this);
8425         });
8426         this.fireEvent('rowsrendered', this);
8427         
8428         this.autoSize();
8429     },
8430     
8431     
8432     onUpdate : function(ds,record)
8433     {
8434         this.refreshRow(record);
8435         this.autoSize();
8436     },
8437     
8438     onRemove : function(ds, record, index, isUpdate){
8439         if(isUpdate !== true){
8440             this.fireEvent("beforerowremoved", this, index, record);
8441         }
8442         var bt = this.mainBody.dom;
8443         
8444         var rows = this.el.select('tbody > tr', true).elements;
8445         
8446         if(typeof(rows[index]) != 'undefined'){
8447             bt.removeChild(rows[index].dom);
8448         }
8449         
8450 //        if(bt.rows[index]){
8451 //            bt.removeChild(bt.rows[index]);
8452 //        }
8453         
8454         if(isUpdate !== true){
8455             //this.stripeRows(index);
8456             //this.syncRowHeights(index, index);
8457             //this.layout();
8458             this.fireEvent("rowremoved", this, index, record);
8459         }
8460     },
8461     
8462     onAdd : function(ds, records, rowIndex)
8463     {
8464         //Roo.log('on Add called');
8465         // - note this does not handle multiple adding very well..
8466         var bt = this.mainBody.dom;
8467         for (var i =0 ; i < records.length;i++) {
8468             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8469             //Roo.log(records[i]);
8470             //Roo.log(this.store.getAt(rowIndex+i));
8471             this.insertRow(this.store, rowIndex + i, false);
8472             return;
8473         }
8474         
8475     },
8476     
8477     
8478     refreshRow : function(record){
8479         var ds = this.store, index;
8480         if(typeof record == 'number'){
8481             index = record;
8482             record = ds.getAt(index);
8483         }else{
8484             index = ds.indexOf(record);
8485             if (index < 0) {
8486                 return; // should not happen - but seems to 
8487             }
8488         }
8489         this.insertRow(ds, index, true);
8490         this.autoSize();
8491         this.onRemove(ds, record, index+1, true);
8492         this.autoSize();
8493         //this.syncRowHeights(index, index);
8494         //this.layout();
8495         this.fireEvent("rowupdated", this, index, record);
8496     },
8497     
8498     insertRow : function(dm, rowIndex, isUpdate){
8499         
8500         if(!isUpdate){
8501             this.fireEvent("beforerowsinserted", this, rowIndex);
8502         }
8503             //var s = this.getScrollState();
8504         var row = this.renderRow(this.cm, this.store, rowIndex);
8505         // insert before rowIndex..
8506         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8507         
8508         var _this = this;
8509                 
8510         if(row.cellObjects.length){
8511             Roo.each(row.cellObjects, function(r){
8512                 _this.renderCellObject(r);
8513             })
8514         }
8515             
8516         if(!isUpdate){
8517             this.fireEvent("rowsinserted", this, rowIndex);
8518             //this.syncRowHeights(firstRow, lastRow);
8519             //this.stripeRows(firstRow);
8520             //this.layout();
8521         }
8522         
8523     },
8524     
8525     
8526     getRowDom : function(rowIndex)
8527     {
8528         var rows = this.el.select('tbody > tr', true).elements;
8529         
8530         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8531         
8532     },
8533     // returns the object tree for a tr..
8534   
8535     
8536     renderRow : function(cm, ds, rowIndex) 
8537     {
8538         var d = ds.getAt(rowIndex);
8539         
8540         var row = {
8541             tag : 'tr',
8542             cls : 'x-row-' + rowIndex,
8543             cn : []
8544         };
8545             
8546         var cellObjects = [];
8547         
8548         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8549             var config = cm.config[i];
8550             
8551             var renderer = cm.getRenderer(i);
8552             var value = '';
8553             var id = false;
8554             
8555             if(typeof(renderer) !== 'undefined'){
8556                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8557             }
8558             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8559             // and are rendered into the cells after the row is rendered - using the id for the element.
8560             
8561             if(typeof(value) === 'object'){
8562                 id = Roo.id();
8563                 cellObjects.push({
8564                     container : id,
8565                     cfg : value 
8566                 })
8567             }
8568             
8569             var rowcfg = {
8570                 record: d,
8571                 rowIndex : rowIndex,
8572                 colIndex : i,
8573                 rowClass : ''
8574             };
8575
8576             this.fireEvent('rowclass', this, rowcfg);
8577             
8578             var td = {
8579                 tag: 'td',
8580                 cls : rowcfg.rowClass + ' x-col-' + i,
8581                 style: '',
8582                 html: (typeof(value) === 'object') ? '' : value
8583             };
8584             
8585             if (id) {
8586                 td.id = id;
8587             }
8588             
8589             if(typeof(config.colspan) != 'undefined'){
8590                 td.colspan = config.colspan;
8591             }
8592             
8593             if(typeof(config.hidden) != 'undefined' && config.hidden){
8594                 td.style += ' display:none;';
8595             }
8596             
8597             if(typeof(config.align) != 'undefined' && config.align.length){
8598                 td.style += ' text-align:' + config.align + ';';
8599             }
8600             if(typeof(config.valign) != 'undefined' && config.valign.length){
8601                 td.style += ' vertical-align:' + config.valign + ';';
8602             }
8603             
8604             if(typeof(config.width) != 'undefined'){
8605                 td.style += ' width:' +  config.width + 'px;';
8606             }
8607             
8608             if(typeof(config.cursor) != 'undefined'){
8609                 td.style += ' cursor:' +  config.cursor + ';';
8610             }
8611             
8612             if(typeof(config.cls) != 'undefined'){
8613                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8614             }
8615             
8616             ['xs','sm','md','lg'].map(function(size){
8617                 
8618                 if(typeof(config[size]) == 'undefined'){
8619                     return;
8620                 }
8621                 
8622                 
8623                   
8624                 if (!config[size]) { // 0 = hidden
8625                     // BS 4 '0' is treated as hide that column and below.
8626                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8627                     return;
8628                 }
8629                 
8630                 td.cls += ' col-' + size + '-' + config[size] + (
8631                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8632                 );
8633                  
8634
8635             });
8636             
8637             row.cn.push(td);
8638            
8639         }
8640         
8641         row.cellObjects = cellObjects;
8642         
8643         return row;
8644           
8645     },
8646     
8647     
8648     
8649     onBeforeLoad : function()
8650     {
8651         
8652     },
8653      /**
8654      * Remove all rows
8655      */
8656     clear : function()
8657     {
8658         this.el.select('tbody', true).first().dom.innerHTML = '';
8659     },
8660     /**
8661      * Show or hide a row.
8662      * @param {Number} rowIndex to show or hide
8663      * @param {Boolean} state hide
8664      */
8665     setRowVisibility : function(rowIndex, state)
8666     {
8667         var bt = this.mainBody.dom;
8668         
8669         var rows = this.el.select('tbody > tr', true).elements;
8670         
8671         if(typeof(rows[rowIndex]) == 'undefined'){
8672             return;
8673         }
8674         rows[rowIndex].dom.style.display = state ? '' : 'none';
8675     },
8676     
8677     
8678     getSelectionModel : function(){
8679         if(!this.selModel){
8680             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8681         }
8682         return this.selModel;
8683     },
8684     /*
8685      * Render the Roo.bootstrap object from renderder
8686      */
8687     renderCellObject : function(r)
8688     {
8689         var _this = this;
8690         
8691         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8692         
8693         var t = r.cfg.render(r.container);
8694         
8695         if(r.cfg.cn){
8696             Roo.each(r.cfg.cn, function(c){
8697                 var child = {
8698                     container: t.getChildContainer(),
8699                     cfg: c
8700                 };
8701                 _this.renderCellObject(child);
8702             })
8703         }
8704     },
8705     
8706     getRowIndex : function(row)
8707     {
8708         var rowIndex = -1;
8709         
8710         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8711             if(el != row){
8712                 return;
8713             }
8714             
8715             rowIndex = index;
8716         });
8717         
8718         return rowIndex;
8719     },
8720      /**
8721      * Returns the grid's underlying element = used by panel.Grid
8722      * @return {Element} The element
8723      */
8724     getGridEl : function(){
8725         return this.el;
8726     },
8727      /**
8728      * Forces a resize - used by panel.Grid
8729      * @return {Element} The element
8730      */
8731     autoSize : function()
8732     {
8733         //var ctr = Roo.get(this.container.dom.parentElement);
8734         var ctr = Roo.get(this.el.dom);
8735         
8736         var thd = this.getGridEl().select('thead',true).first();
8737         var tbd = this.getGridEl().select('tbody', true).first();
8738         var tfd = this.getGridEl().select('tfoot', true).first();
8739         
8740         var cw = ctr.getWidth();
8741         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
8742         
8743         if (tbd) {
8744             
8745             tbd.setWidth(ctr.getWidth());
8746             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8747             // this needs fixing for various usage - currently only hydra job advers I think..
8748             //tdb.setHeight(
8749             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8750             //); 
8751             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8752             cw -= barsize;
8753         }
8754         cw = Math.max(cw, this.totalWidth);
8755         this.getGridEl().select('tbody tr',true).setWidth(cw);
8756         
8757         // resize 'expandable coloumn?
8758         
8759         return; // we doe not have a view in this design..
8760         
8761     },
8762     onBodyScroll: function()
8763     {
8764         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8765         if(this.mainHead){
8766             this.mainHead.setStyle({
8767                 'position' : 'relative',
8768                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8769             });
8770         }
8771         
8772         if(this.lazyLoad){
8773             
8774             var scrollHeight = this.mainBody.dom.scrollHeight;
8775             
8776             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8777             
8778             var height = this.mainBody.getHeight();
8779             
8780             if(scrollHeight - height == scrollTop) {
8781                 
8782                 var total = this.ds.getTotalCount();
8783                 
8784                 if(this.footer.cursor + this.footer.pageSize < total){
8785                     
8786                     this.footer.ds.load({
8787                         params : {
8788                             start : this.footer.cursor + this.footer.pageSize,
8789                             limit : this.footer.pageSize
8790                         },
8791                         add : true
8792                     });
8793                 }
8794             }
8795             
8796         }
8797     },
8798     
8799     onHeaderChange : function()
8800     {
8801         var header = this.renderHeader();
8802         var table = this.el.select('table', true).first();
8803         
8804         this.mainHead.remove();
8805         this.mainHead = table.createChild(header, this.mainBody, false);
8806     },
8807     
8808     onHiddenChange : function(colModel, colIndex, hidden)
8809     {
8810         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8811         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8812         
8813         this.CSS.updateRule(thSelector, "display", "");
8814         this.CSS.updateRule(tdSelector, "display", "");
8815         
8816         if(hidden){
8817             this.CSS.updateRule(thSelector, "display", "none");
8818             this.CSS.updateRule(tdSelector, "display", "none");
8819         }
8820         
8821         this.onHeaderChange();
8822         this.onLoad();
8823     },
8824     
8825     setColumnWidth: function(col_index, width)
8826     {
8827         // width = "md-2 xs-2..."
8828         if(!this.colModel.config[col_index]) {
8829             return;
8830         }
8831         
8832         var w = width.split(" ");
8833         
8834         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8835         
8836         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8837         
8838         
8839         for(var j = 0; j < w.length; j++) {
8840             
8841             if(!w[j]) {
8842                 continue;
8843             }
8844             
8845             var size_cls = w[j].split("-");
8846             
8847             if(!Number.isInteger(size_cls[1] * 1)) {
8848                 continue;
8849             }
8850             
8851             if(!this.colModel.config[col_index][size_cls[0]]) {
8852                 continue;
8853             }
8854             
8855             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8856                 continue;
8857             }
8858             
8859             h_row[0].classList.replace(
8860                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8861                 "col-"+size_cls[0]+"-"+size_cls[1]
8862             );
8863             
8864             for(var i = 0; i < rows.length; i++) {
8865                 
8866                 var size_cls = w[j].split("-");
8867                 
8868                 if(!Number.isInteger(size_cls[1] * 1)) {
8869                     continue;
8870                 }
8871                 
8872                 if(!this.colModel.config[col_index][size_cls[0]]) {
8873                     continue;
8874                 }
8875                 
8876                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8877                     continue;
8878                 }
8879                 
8880                 rows[i].classList.replace(
8881                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8882                     "col-"+size_cls[0]+"-"+size_cls[1]
8883                 );
8884             }
8885             
8886             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8887         }
8888     }
8889 });
8890
8891  
8892
8893  /*
8894  * - LGPL
8895  *
8896  * table cell
8897  * 
8898  */
8899
8900 /**
8901  * @class Roo.bootstrap.TableCell
8902  * @extends Roo.bootstrap.Component
8903  * Bootstrap TableCell class
8904  * @cfg {String} html cell contain text
8905  * @cfg {String} cls cell class
8906  * @cfg {String} tag cell tag (td|th) default td
8907  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8908  * @cfg {String} align Aligns the content in a cell
8909  * @cfg {String} axis Categorizes cells
8910  * @cfg {String} bgcolor Specifies the background color of a cell
8911  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8912  * @cfg {Number} colspan Specifies the number of columns a cell should span
8913  * @cfg {String} headers Specifies one or more header cells a cell is related to
8914  * @cfg {Number} height Sets the height of a cell
8915  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8916  * @cfg {Number} rowspan Sets the number of rows a cell should span
8917  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8918  * @cfg {String} valign Vertical aligns the content in a cell
8919  * @cfg {Number} width Specifies the width of a cell
8920  * 
8921  * @constructor
8922  * Create a new TableCell
8923  * @param {Object} config The config object
8924  */
8925
8926 Roo.bootstrap.TableCell = function(config){
8927     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8928 };
8929
8930 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
8931     
8932     html: false,
8933     cls: false,
8934     tag: false,
8935     abbr: false,
8936     align: false,
8937     axis: false,
8938     bgcolor: false,
8939     charoff: false,
8940     colspan: false,
8941     headers: false,
8942     height: false,
8943     nowrap: false,
8944     rowspan: false,
8945     scope: false,
8946     valign: false,
8947     width: false,
8948     
8949     
8950     getAutoCreate : function(){
8951         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8952         
8953         cfg = {
8954             tag: 'td'
8955         };
8956         
8957         if(this.tag){
8958             cfg.tag = this.tag;
8959         }
8960         
8961         if (this.html) {
8962             cfg.html=this.html
8963         }
8964         if (this.cls) {
8965             cfg.cls=this.cls
8966         }
8967         if (this.abbr) {
8968             cfg.abbr=this.abbr
8969         }
8970         if (this.align) {
8971             cfg.align=this.align
8972         }
8973         if (this.axis) {
8974             cfg.axis=this.axis
8975         }
8976         if (this.bgcolor) {
8977             cfg.bgcolor=this.bgcolor
8978         }
8979         if (this.charoff) {
8980             cfg.charoff=this.charoff
8981         }
8982         if (this.colspan) {
8983             cfg.colspan=this.colspan
8984         }
8985         if (this.headers) {
8986             cfg.headers=this.headers
8987         }
8988         if (this.height) {
8989             cfg.height=this.height
8990         }
8991         if (this.nowrap) {
8992             cfg.nowrap=this.nowrap
8993         }
8994         if (this.rowspan) {
8995             cfg.rowspan=this.rowspan
8996         }
8997         if (this.scope) {
8998             cfg.scope=this.scope
8999         }
9000         if (this.valign) {
9001             cfg.valign=this.valign
9002         }
9003         if (this.width) {
9004             cfg.width=this.width
9005         }
9006         
9007         
9008         return cfg;
9009     }
9010    
9011 });
9012
9013  
9014
9015  /*
9016  * - LGPL
9017  *
9018  * table row
9019  * 
9020  */
9021
9022 /**
9023  * @class Roo.bootstrap.TableRow
9024  * @extends Roo.bootstrap.Component
9025  * Bootstrap TableRow class
9026  * @cfg {String} cls row class
9027  * @cfg {String} align Aligns the content in a table row
9028  * @cfg {String} bgcolor Specifies a background color for a table row
9029  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9030  * @cfg {String} valign Vertical aligns the content in a table row
9031  * 
9032  * @constructor
9033  * Create a new TableRow
9034  * @param {Object} config The config object
9035  */
9036
9037 Roo.bootstrap.TableRow = function(config){
9038     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9039 };
9040
9041 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9042     
9043     cls: false,
9044     align: false,
9045     bgcolor: false,
9046     charoff: false,
9047     valign: false,
9048     
9049     getAutoCreate : function(){
9050         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9051         
9052         cfg = {
9053             tag: 'tr'
9054         };
9055             
9056         if(this.cls){
9057             cfg.cls = this.cls;
9058         }
9059         if(this.align){
9060             cfg.align = this.align;
9061         }
9062         if(this.bgcolor){
9063             cfg.bgcolor = this.bgcolor;
9064         }
9065         if(this.charoff){
9066             cfg.charoff = this.charoff;
9067         }
9068         if(this.valign){
9069             cfg.valign = this.valign;
9070         }
9071         
9072         return cfg;
9073     }
9074    
9075 });
9076
9077  
9078
9079  /*
9080  * - LGPL
9081  *
9082  * table body
9083  * 
9084  */
9085
9086 /**
9087  * @class Roo.bootstrap.TableBody
9088  * @extends Roo.bootstrap.Component
9089  * Bootstrap TableBody class
9090  * @cfg {String} cls element class
9091  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9092  * @cfg {String} align Aligns the content inside the element
9093  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9094  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9095  * 
9096  * @constructor
9097  * Create a new TableBody
9098  * @param {Object} config The config object
9099  */
9100
9101 Roo.bootstrap.TableBody = function(config){
9102     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9103 };
9104
9105 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9106     
9107     cls: false,
9108     tag: false,
9109     align: false,
9110     charoff: false,
9111     valign: false,
9112     
9113     getAutoCreate : function(){
9114         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9115         
9116         cfg = {
9117             tag: 'tbody'
9118         };
9119             
9120         if (this.cls) {
9121             cfg.cls=this.cls
9122         }
9123         if(this.tag){
9124             cfg.tag = this.tag;
9125         }
9126         
9127         if(this.align){
9128             cfg.align = this.align;
9129         }
9130         if(this.charoff){
9131             cfg.charoff = this.charoff;
9132         }
9133         if(this.valign){
9134             cfg.valign = this.valign;
9135         }
9136         
9137         return cfg;
9138     }
9139     
9140     
9141 //    initEvents : function()
9142 //    {
9143 //        
9144 //        if(!this.store){
9145 //            return;
9146 //        }
9147 //        
9148 //        this.store = Roo.factory(this.store, Roo.data);
9149 //        this.store.on('load', this.onLoad, this);
9150 //        
9151 //        this.store.load();
9152 //        
9153 //    },
9154 //    
9155 //    onLoad: function () 
9156 //    {   
9157 //        this.fireEvent('load', this);
9158 //    }
9159 //    
9160 //   
9161 });
9162
9163  
9164
9165  /*
9166  * Based on:
9167  * Ext JS Library 1.1.1
9168  * Copyright(c) 2006-2007, Ext JS, LLC.
9169  *
9170  * Originally Released Under LGPL - original licence link has changed is not relivant.
9171  *
9172  * Fork - LGPL
9173  * <script type="text/javascript">
9174  */
9175
9176 // as we use this in bootstrap.
9177 Roo.namespace('Roo.form');
9178  /**
9179  * @class Roo.form.Action
9180  * Internal Class used to handle form actions
9181  * @constructor
9182  * @param {Roo.form.BasicForm} el The form element or its id
9183  * @param {Object} config Configuration options
9184  */
9185
9186  
9187  
9188 // define the action interface
9189 Roo.form.Action = function(form, options){
9190     this.form = form;
9191     this.options = options || {};
9192 };
9193 /**
9194  * Client Validation Failed
9195  * @const 
9196  */
9197 Roo.form.Action.CLIENT_INVALID = 'client';
9198 /**
9199  * Server Validation Failed
9200  * @const 
9201  */
9202 Roo.form.Action.SERVER_INVALID = 'server';
9203  /**
9204  * Connect to Server Failed
9205  * @const 
9206  */
9207 Roo.form.Action.CONNECT_FAILURE = 'connect';
9208 /**
9209  * Reading Data from Server Failed
9210  * @const 
9211  */
9212 Roo.form.Action.LOAD_FAILURE = 'load';
9213
9214 Roo.form.Action.prototype = {
9215     type : 'default',
9216     failureType : undefined,
9217     response : undefined,
9218     result : undefined,
9219
9220     // interface method
9221     run : function(options){
9222
9223     },
9224
9225     // interface method
9226     success : function(response){
9227
9228     },
9229
9230     // interface method
9231     handleResponse : function(response){
9232
9233     },
9234
9235     // default connection failure
9236     failure : function(response){
9237         
9238         this.response = response;
9239         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9240         this.form.afterAction(this, false);
9241     },
9242
9243     processResponse : function(response){
9244         this.response = response;
9245         if(!response.responseText){
9246             return true;
9247         }
9248         this.result = this.handleResponse(response);
9249         return this.result;
9250     },
9251
9252     // utility functions used internally
9253     getUrl : function(appendParams){
9254         var url = this.options.url || this.form.url || this.form.el.dom.action;
9255         if(appendParams){
9256             var p = this.getParams();
9257             if(p){
9258                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9259             }
9260         }
9261         return url;
9262     },
9263
9264     getMethod : function(){
9265         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9266     },
9267
9268     getParams : function(){
9269         var bp = this.form.baseParams;
9270         var p = this.options.params;
9271         if(p){
9272             if(typeof p == "object"){
9273                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9274             }else if(typeof p == 'string' && bp){
9275                 p += '&' + Roo.urlEncode(bp);
9276             }
9277         }else if(bp){
9278             p = Roo.urlEncode(bp);
9279         }
9280         return p;
9281     },
9282
9283     createCallback : function(){
9284         return {
9285             success: this.success,
9286             failure: this.failure,
9287             scope: this,
9288             timeout: (this.form.timeout*1000),
9289             upload: this.form.fileUpload ? this.success : undefined
9290         };
9291     }
9292 };
9293
9294 Roo.form.Action.Submit = function(form, options){
9295     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9296 };
9297
9298 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9299     type : 'submit',
9300
9301     haveProgress : false,
9302     uploadComplete : false,
9303     
9304     // uploadProgress indicator.
9305     uploadProgress : function()
9306     {
9307         if (!this.form.progressUrl) {
9308             return;
9309         }
9310         
9311         if (!this.haveProgress) {
9312             Roo.MessageBox.progress("Uploading", "Uploading");
9313         }
9314         if (this.uploadComplete) {
9315            Roo.MessageBox.hide();
9316            return;
9317         }
9318         
9319         this.haveProgress = true;
9320    
9321         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9322         
9323         var c = new Roo.data.Connection();
9324         c.request({
9325             url : this.form.progressUrl,
9326             params: {
9327                 id : uid
9328             },
9329             method: 'GET',
9330             success : function(req){
9331                //console.log(data);
9332                 var rdata = false;
9333                 var edata;
9334                 try  {
9335                    rdata = Roo.decode(req.responseText)
9336                 } catch (e) {
9337                     Roo.log("Invalid data from server..");
9338                     Roo.log(edata);
9339                     return;
9340                 }
9341                 if (!rdata || !rdata.success) {
9342                     Roo.log(rdata);
9343                     Roo.MessageBox.alert(Roo.encode(rdata));
9344                     return;
9345                 }
9346                 var data = rdata.data;
9347                 
9348                 if (this.uploadComplete) {
9349                    Roo.MessageBox.hide();
9350                    return;
9351                 }
9352                    
9353                 if (data){
9354                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9355                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9356                     );
9357                 }
9358                 this.uploadProgress.defer(2000,this);
9359             },
9360        
9361             failure: function(data) {
9362                 Roo.log('progress url failed ');
9363                 Roo.log(data);
9364             },
9365             scope : this
9366         });
9367            
9368     },
9369     
9370     
9371     run : function()
9372     {
9373         // run get Values on the form, so it syncs any secondary forms.
9374         this.form.getValues();
9375         
9376         var o = this.options;
9377         var method = this.getMethod();
9378         var isPost = method == 'POST';
9379         if(o.clientValidation === false || this.form.isValid()){
9380             
9381             if (this.form.progressUrl) {
9382                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9383                     (new Date() * 1) + '' + Math.random());
9384                     
9385             } 
9386             
9387             
9388             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9389                 form:this.form.el.dom,
9390                 url:this.getUrl(!isPost),
9391                 method: method,
9392                 params:isPost ? this.getParams() : null,
9393                 isUpload: this.form.fileUpload,
9394                 formData : this.form.formData
9395             }));
9396             
9397             this.uploadProgress();
9398
9399         }else if (o.clientValidation !== false){ // client validation failed
9400             this.failureType = Roo.form.Action.CLIENT_INVALID;
9401             this.form.afterAction(this, false);
9402         }
9403     },
9404
9405     success : function(response)
9406     {
9407         this.uploadComplete= true;
9408         if (this.haveProgress) {
9409             Roo.MessageBox.hide();
9410         }
9411         
9412         
9413         var result = this.processResponse(response);
9414         if(result === true || result.success){
9415             this.form.afterAction(this, true);
9416             return;
9417         }
9418         if(result.errors){
9419             this.form.markInvalid(result.errors);
9420             this.failureType = Roo.form.Action.SERVER_INVALID;
9421         }
9422         this.form.afterAction(this, false);
9423     },
9424     failure : function(response)
9425     {
9426         this.uploadComplete= true;
9427         if (this.haveProgress) {
9428             Roo.MessageBox.hide();
9429         }
9430         
9431         this.response = response;
9432         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9433         this.form.afterAction(this, false);
9434     },
9435     
9436     handleResponse : function(response){
9437         if(this.form.errorReader){
9438             var rs = this.form.errorReader.read(response);
9439             var errors = [];
9440             if(rs.records){
9441                 for(var i = 0, len = rs.records.length; i < len; i++) {
9442                     var r = rs.records[i];
9443                     errors[i] = r.data;
9444                 }
9445             }
9446             if(errors.length < 1){
9447                 errors = null;
9448             }
9449             return {
9450                 success : rs.success,
9451                 errors : errors
9452             };
9453         }
9454         var ret = false;
9455         try {
9456             ret = Roo.decode(response.responseText);
9457         } catch (e) {
9458             ret = {
9459                 success: false,
9460                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9461                 errors : []
9462             };
9463         }
9464         return ret;
9465         
9466     }
9467 });
9468
9469
9470 Roo.form.Action.Load = function(form, options){
9471     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9472     this.reader = this.form.reader;
9473 };
9474
9475 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9476     type : 'load',
9477
9478     run : function(){
9479         
9480         Roo.Ajax.request(Roo.apply(
9481                 this.createCallback(), {
9482                     method:this.getMethod(),
9483                     url:this.getUrl(false),
9484                     params:this.getParams()
9485         }));
9486     },
9487
9488     success : function(response){
9489         
9490         var result = this.processResponse(response);
9491         if(result === true || !result.success || !result.data){
9492             this.failureType = Roo.form.Action.LOAD_FAILURE;
9493             this.form.afterAction(this, false);
9494             return;
9495         }
9496         this.form.clearInvalid();
9497         this.form.setValues(result.data);
9498         this.form.afterAction(this, true);
9499     },
9500
9501     handleResponse : function(response){
9502         if(this.form.reader){
9503             var rs = this.form.reader.read(response);
9504             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9505             return {
9506                 success : rs.success,
9507                 data : data
9508             };
9509         }
9510         return Roo.decode(response.responseText);
9511     }
9512 });
9513
9514 Roo.form.Action.ACTION_TYPES = {
9515     'load' : Roo.form.Action.Load,
9516     'submit' : Roo.form.Action.Submit
9517 };/*
9518  * - LGPL
9519  *
9520  * form
9521  *
9522  */
9523
9524 /**
9525  * @class Roo.bootstrap.Form
9526  * @extends Roo.bootstrap.Component
9527  * Bootstrap Form class
9528  * @cfg {String} method  GET | POST (default POST)
9529  * @cfg {String} labelAlign top | left (default top)
9530  * @cfg {String} align left  | right - for navbars
9531  * @cfg {Boolean} loadMask load mask when submit (default true)
9532
9533  *
9534  * @constructor
9535  * Create a new Form
9536  * @param {Object} config The config object
9537  */
9538
9539
9540 Roo.bootstrap.Form = function(config){
9541     
9542     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9543     
9544     Roo.bootstrap.Form.popover.apply();
9545     
9546     this.addEvents({
9547         /**
9548          * @event clientvalidation
9549          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9550          * @param {Form} this
9551          * @param {Boolean} valid true if the form has passed client-side validation
9552          */
9553         clientvalidation: true,
9554         /**
9555          * @event beforeaction
9556          * Fires before any action is performed. Return false to cancel the action.
9557          * @param {Form} this
9558          * @param {Action} action The action to be performed
9559          */
9560         beforeaction: true,
9561         /**
9562          * @event actionfailed
9563          * Fires when an action fails.
9564          * @param {Form} this
9565          * @param {Action} action The action that failed
9566          */
9567         actionfailed : true,
9568         /**
9569          * @event actioncomplete
9570          * Fires when an action is completed.
9571          * @param {Form} this
9572          * @param {Action} action The action that completed
9573          */
9574         actioncomplete : true
9575     });
9576 };
9577
9578 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9579
9580      /**
9581      * @cfg {String} method
9582      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9583      */
9584     method : 'POST',
9585     /**
9586      * @cfg {String} url
9587      * The URL to use for form actions if one isn't supplied in the action options.
9588      */
9589     /**
9590      * @cfg {Boolean} fileUpload
9591      * Set to true if this form is a file upload.
9592      */
9593
9594     /**
9595      * @cfg {Object} baseParams
9596      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9597      */
9598
9599     /**
9600      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9601      */
9602     timeout: 30,
9603     /**
9604      * @cfg {Sting} align (left|right) for navbar forms
9605      */
9606     align : 'left',
9607
9608     // private
9609     activeAction : null,
9610
9611     /**
9612      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9613      * element by passing it or its id or mask the form itself by passing in true.
9614      * @type Mixed
9615      */
9616     waitMsgTarget : false,
9617
9618     loadMask : true,
9619     
9620     /**
9621      * @cfg {Boolean} errorMask (true|false) default false
9622      */
9623     errorMask : false,
9624     
9625     /**
9626      * @cfg {Number} maskOffset Default 100
9627      */
9628     maskOffset : 100,
9629     
9630     /**
9631      * @cfg {Boolean} maskBody
9632      */
9633     maskBody : false,
9634
9635     getAutoCreate : function(){
9636
9637         var cfg = {
9638             tag: 'form',
9639             method : this.method || 'POST',
9640             id : this.id || Roo.id(),
9641             cls : ''
9642         };
9643         if (this.parent().xtype.match(/^Nav/)) {
9644             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9645
9646         }
9647
9648         if (this.labelAlign == 'left' ) {
9649             cfg.cls += ' form-horizontal';
9650         }
9651
9652
9653         return cfg;
9654     },
9655     initEvents : function()
9656     {
9657         this.el.on('submit', this.onSubmit, this);
9658         // this was added as random key presses on the form where triggering form submit.
9659         this.el.on('keypress', function(e) {
9660             if (e.getCharCode() != 13) {
9661                 return true;
9662             }
9663             // we might need to allow it for textareas.. and some other items.
9664             // check e.getTarget().
9665
9666             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9667                 return true;
9668             }
9669
9670             Roo.log("keypress blocked");
9671
9672             e.preventDefault();
9673             return false;
9674         });
9675         
9676     },
9677     // private
9678     onSubmit : function(e){
9679         e.stopEvent();
9680     },
9681
9682      /**
9683      * Returns true if client-side validation on the form is successful.
9684      * @return Boolean
9685      */
9686     isValid : function(){
9687         var items = this.getItems();
9688         var valid = true;
9689         var target = false;
9690         
9691         items.each(function(f){
9692             
9693             if(f.validate()){
9694                 return;
9695             }
9696             
9697             Roo.log('invalid field: ' + f.name);
9698             
9699             valid = false;
9700
9701             if(!target && f.el.isVisible(true)){
9702                 target = f;
9703             }
9704            
9705         });
9706         
9707         if(this.errorMask && !valid){
9708             Roo.bootstrap.Form.popover.mask(this, target);
9709         }
9710         
9711         return valid;
9712     },
9713     
9714     /**
9715      * Returns true if any fields in this form have changed since their original load.
9716      * @return Boolean
9717      */
9718     isDirty : function(){
9719         var dirty = false;
9720         var items = this.getItems();
9721         items.each(function(f){
9722            if(f.isDirty()){
9723                dirty = true;
9724                return false;
9725            }
9726            return true;
9727         });
9728         return dirty;
9729     },
9730      /**
9731      * Performs a predefined action (submit or load) or custom actions you define on this form.
9732      * @param {String} actionName The name of the action type
9733      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9734      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9735      * accept other config options):
9736      * <pre>
9737 Property          Type             Description
9738 ----------------  ---------------  ----------------------------------------------------------------------------------
9739 url               String           The url for the action (defaults to the form's url)
9740 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9741 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9742 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9743                                    validate the form on the client (defaults to false)
9744      * </pre>
9745      * @return {BasicForm} this
9746      */
9747     doAction : function(action, options){
9748         if(typeof action == 'string'){
9749             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9750         }
9751         if(this.fireEvent('beforeaction', this, action) !== false){
9752             this.beforeAction(action);
9753             action.run.defer(100, action);
9754         }
9755         return this;
9756     },
9757
9758     // private
9759     beforeAction : function(action){
9760         var o = action.options;
9761         
9762         if(this.loadMask){
9763             
9764             if(this.maskBody){
9765                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9766             } else {
9767                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9768             }
9769         }
9770         // not really supported yet.. ??
9771
9772         //if(this.waitMsgTarget === true){
9773         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9774         //}else if(this.waitMsgTarget){
9775         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9776         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9777         //}else {
9778         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9779        // }
9780
9781     },
9782
9783     // private
9784     afterAction : function(action, success){
9785         this.activeAction = null;
9786         var o = action.options;
9787
9788         if(this.loadMask){
9789             
9790             if(this.maskBody){
9791                 Roo.get(document.body).unmask();
9792             } else {
9793                 this.el.unmask();
9794             }
9795         }
9796         
9797         //if(this.waitMsgTarget === true){
9798 //            this.el.unmask();
9799         //}else if(this.waitMsgTarget){
9800         //    this.waitMsgTarget.unmask();
9801         //}else{
9802         //    Roo.MessageBox.updateProgress(1);
9803         //    Roo.MessageBox.hide();
9804        // }
9805         //
9806         if(success){
9807             if(o.reset){
9808                 this.reset();
9809             }
9810             Roo.callback(o.success, o.scope, [this, action]);
9811             this.fireEvent('actioncomplete', this, action);
9812
9813         }else{
9814
9815             // failure condition..
9816             // we have a scenario where updates need confirming.
9817             // eg. if a locking scenario exists..
9818             // we look for { errors : { needs_confirm : true }} in the response.
9819             if (
9820                 (typeof(action.result) != 'undefined')  &&
9821                 (typeof(action.result.errors) != 'undefined')  &&
9822                 (typeof(action.result.errors.needs_confirm) != 'undefined')
9823            ){
9824                 var _t = this;
9825                 Roo.log("not supported yet");
9826                  /*
9827
9828                 Roo.MessageBox.confirm(
9829                     "Change requires confirmation",
9830                     action.result.errorMsg,
9831                     function(r) {
9832                         if (r != 'yes') {
9833                             return;
9834                         }
9835                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
9836                     }
9837
9838                 );
9839                 */
9840
9841
9842                 return;
9843             }
9844
9845             Roo.callback(o.failure, o.scope, [this, action]);
9846             // show an error message if no failed handler is set..
9847             if (!this.hasListener('actionfailed')) {
9848                 Roo.log("need to add dialog support");
9849                 /*
9850                 Roo.MessageBox.alert("Error",
9851                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9852                         action.result.errorMsg :
9853                         "Saving Failed, please check your entries or try again"
9854                 );
9855                 */
9856             }
9857
9858             this.fireEvent('actionfailed', this, action);
9859         }
9860
9861     },
9862     /**
9863      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9864      * @param {String} id The value to search for
9865      * @return Field
9866      */
9867     findField : function(id){
9868         var items = this.getItems();
9869         var field = items.get(id);
9870         if(!field){
9871              items.each(function(f){
9872                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9873                     field = f;
9874                     return false;
9875                 }
9876                 return true;
9877             });
9878         }
9879         return field || null;
9880     },
9881      /**
9882      * Mark fields in this form invalid in bulk.
9883      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9884      * @return {BasicForm} this
9885      */
9886     markInvalid : function(errors){
9887         if(errors instanceof Array){
9888             for(var i = 0, len = errors.length; i < len; i++){
9889                 var fieldError = errors[i];
9890                 var f = this.findField(fieldError.id);
9891                 if(f){
9892                     f.markInvalid(fieldError.msg);
9893                 }
9894             }
9895         }else{
9896             var field, id;
9897             for(id in errors){
9898                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9899                     field.markInvalid(errors[id]);
9900                 }
9901             }
9902         }
9903         //Roo.each(this.childForms || [], function (f) {
9904         //    f.markInvalid(errors);
9905         //});
9906
9907         return this;
9908     },
9909
9910     /**
9911      * Set values for fields in this form in bulk.
9912      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9913      * @return {BasicForm} this
9914      */
9915     setValues : function(values){
9916         if(values instanceof Array){ // array of objects
9917             for(var i = 0, len = values.length; i < len; i++){
9918                 var v = values[i];
9919                 var f = this.findField(v.id);
9920                 if(f){
9921                     f.setValue(v.value);
9922                     if(this.trackResetOnLoad){
9923                         f.originalValue = f.getValue();
9924                     }
9925                 }
9926             }
9927         }else{ // object hash
9928             var field, id;
9929             for(id in values){
9930                 if(typeof values[id] != 'function' && (field = this.findField(id))){
9931
9932                     if (field.setFromData &&
9933                         field.valueField &&
9934                         field.displayField &&
9935                         // combos' with local stores can
9936                         // be queried via setValue()
9937                         // to set their value..
9938                         (field.store && !field.store.isLocal)
9939                         ) {
9940                         // it's a combo
9941                         var sd = { };
9942                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9943                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9944                         field.setFromData(sd);
9945
9946                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9947                         
9948                         field.setFromData(values);
9949                         
9950                     } else {
9951                         field.setValue(values[id]);
9952                     }
9953
9954
9955                     if(this.trackResetOnLoad){
9956                         field.originalValue = field.getValue();
9957                     }
9958                 }
9959             }
9960         }
9961
9962         //Roo.each(this.childForms || [], function (f) {
9963         //    f.setValues(values);
9964         //});
9965
9966         return this;
9967     },
9968
9969     /**
9970      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9971      * they are returned as an array.
9972      * @param {Boolean} asString
9973      * @return {Object}
9974      */
9975     getValues : function(asString){
9976         //if (this.childForms) {
9977             // copy values from the child forms
9978         //    Roo.each(this.childForms, function (f) {
9979         //        this.setValues(f.getValues());
9980         //    }, this);
9981         //}
9982
9983
9984
9985         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9986         if(asString === true){
9987             return fs;
9988         }
9989         return Roo.urlDecode(fs);
9990     },
9991
9992     /**
9993      * Returns the fields in this form as an object with key/value pairs.
9994      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9995      * @return {Object}
9996      */
9997     getFieldValues : function(with_hidden)
9998     {
9999         var items = this.getItems();
10000         var ret = {};
10001         items.each(function(f){
10002             
10003             if (!f.getName()) {
10004                 return;
10005             }
10006             
10007             var v = f.getValue();
10008             
10009             if (f.inputType =='radio') {
10010                 if (typeof(ret[f.getName()]) == 'undefined') {
10011                     ret[f.getName()] = ''; // empty..
10012                 }
10013
10014                 if (!f.el.dom.checked) {
10015                     return;
10016
10017                 }
10018                 v = f.el.dom.value;
10019
10020             }
10021             
10022             if(f.xtype == 'MoneyField'){
10023                 ret[f.currencyName] = f.getCurrency();
10024             }
10025
10026             // not sure if this supported any more..
10027             if ((typeof(v) == 'object') && f.getRawValue) {
10028                 v = f.getRawValue() ; // dates..
10029             }
10030             // combo boxes where name != hiddenName...
10031             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10032                 ret[f.name] = f.getRawValue();
10033             }
10034             ret[f.getName()] = v;
10035         });
10036
10037         return ret;
10038     },
10039
10040     /**
10041      * Clears all invalid messages in this form.
10042      * @return {BasicForm} this
10043      */
10044     clearInvalid : function(){
10045         var items = this.getItems();
10046
10047         items.each(function(f){
10048            f.clearInvalid();
10049         });
10050
10051         return this;
10052     },
10053
10054     /**
10055      * Resets this form.
10056      * @return {BasicForm} this
10057      */
10058     reset : function(){
10059         var items = this.getItems();
10060         items.each(function(f){
10061             f.reset();
10062         });
10063
10064         Roo.each(this.childForms || [], function (f) {
10065             f.reset();
10066         });
10067
10068
10069         return this;
10070     },
10071     
10072     getItems : function()
10073     {
10074         var r=new Roo.util.MixedCollection(false, function(o){
10075             return o.id || (o.id = Roo.id());
10076         });
10077         var iter = function(el) {
10078             if (el.inputEl) {
10079                 r.add(el);
10080             }
10081             if (!el.items) {
10082                 return;
10083             }
10084             Roo.each(el.items,function(e) {
10085                 iter(e);
10086             });
10087         };
10088
10089         iter(this);
10090         return r;
10091     },
10092     
10093     hideFields : function(items)
10094     {
10095         Roo.each(items, function(i){
10096             
10097             var f = this.findField(i);
10098             
10099             if(!f){
10100                 return;
10101             }
10102             
10103             f.hide();
10104             
10105         }, this);
10106     },
10107     
10108     showFields : function(items)
10109     {
10110         Roo.each(items, function(i){
10111             
10112             var f = this.findField(i);
10113             
10114             if(!f){
10115                 return;
10116             }
10117             
10118             f.show();
10119             
10120         }, this);
10121     }
10122
10123 });
10124
10125 Roo.apply(Roo.bootstrap.Form, {
10126     
10127     popover : {
10128         
10129         padding : 5,
10130         
10131         isApplied : false,
10132         
10133         isMasked : false,
10134         
10135         form : false,
10136         
10137         target : false,
10138         
10139         toolTip : false,
10140         
10141         intervalID : false,
10142         
10143         maskEl : false,
10144         
10145         apply : function()
10146         {
10147             if(this.isApplied){
10148                 return;
10149             }
10150             
10151             this.maskEl = {
10152                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10153                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10154                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10155                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10156             };
10157             
10158             this.maskEl.top.enableDisplayMode("block");
10159             this.maskEl.left.enableDisplayMode("block");
10160             this.maskEl.bottom.enableDisplayMode("block");
10161             this.maskEl.right.enableDisplayMode("block");
10162             
10163             this.toolTip = new Roo.bootstrap.Tooltip({
10164                 cls : 'roo-form-error-popover',
10165                 alignment : {
10166                     'left' : ['r-l', [-2,0], 'right'],
10167                     'right' : ['l-r', [2,0], 'left'],
10168                     'bottom' : ['tl-bl', [0,2], 'top'],
10169                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10170                 }
10171             });
10172             
10173             this.toolTip.render(Roo.get(document.body));
10174
10175             this.toolTip.el.enableDisplayMode("block");
10176             
10177             Roo.get(document.body).on('click', function(){
10178                 this.unmask();
10179             }, this);
10180             
10181             Roo.get(document.body).on('touchstart', function(){
10182                 this.unmask();
10183             }, this);
10184             
10185             this.isApplied = true
10186         },
10187         
10188         mask : function(form, target)
10189         {
10190             this.form = form;
10191             
10192             this.target = target;
10193             
10194             if(!this.form.errorMask || !target.el){
10195                 return;
10196             }
10197             
10198             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10199             
10200             Roo.log(scrollable);
10201             
10202             var ot = this.target.el.calcOffsetsTo(scrollable);
10203             
10204             var scrollTo = ot[1] - this.form.maskOffset;
10205             
10206             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10207             
10208             scrollable.scrollTo('top', scrollTo);
10209             
10210             var box = this.target.el.getBox();
10211             Roo.log(box);
10212             var zIndex = Roo.bootstrap.Modal.zIndex++;
10213
10214             
10215             this.maskEl.top.setStyle('position', 'absolute');
10216             this.maskEl.top.setStyle('z-index', zIndex);
10217             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10218             this.maskEl.top.setLeft(0);
10219             this.maskEl.top.setTop(0);
10220             this.maskEl.top.show();
10221             
10222             this.maskEl.left.setStyle('position', 'absolute');
10223             this.maskEl.left.setStyle('z-index', zIndex);
10224             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10225             this.maskEl.left.setLeft(0);
10226             this.maskEl.left.setTop(box.y - this.padding);
10227             this.maskEl.left.show();
10228
10229             this.maskEl.bottom.setStyle('position', 'absolute');
10230             this.maskEl.bottom.setStyle('z-index', zIndex);
10231             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10232             this.maskEl.bottom.setLeft(0);
10233             this.maskEl.bottom.setTop(box.bottom + this.padding);
10234             this.maskEl.bottom.show();
10235
10236             this.maskEl.right.setStyle('position', 'absolute');
10237             this.maskEl.right.setStyle('z-index', zIndex);
10238             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10239             this.maskEl.right.setLeft(box.right + this.padding);
10240             this.maskEl.right.setTop(box.y - this.padding);
10241             this.maskEl.right.show();
10242
10243             this.toolTip.bindEl = this.target.el;
10244
10245             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10246
10247             var tip = this.target.blankText;
10248
10249             if(this.target.getValue() !== '' ) {
10250                 
10251                 if (this.target.invalidText.length) {
10252                     tip = this.target.invalidText;
10253                 } else if (this.target.regexText.length){
10254                     tip = this.target.regexText;
10255                 }
10256             }
10257
10258             this.toolTip.show(tip);
10259
10260             this.intervalID = window.setInterval(function() {
10261                 Roo.bootstrap.Form.popover.unmask();
10262             }, 10000);
10263
10264             window.onwheel = function(){ return false;};
10265             
10266             (function(){ this.isMasked = true; }).defer(500, this);
10267             
10268         },
10269         
10270         unmask : function()
10271         {
10272             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10273                 return;
10274             }
10275             
10276             this.maskEl.top.setStyle('position', 'absolute');
10277             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10278             this.maskEl.top.hide();
10279
10280             this.maskEl.left.setStyle('position', 'absolute');
10281             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10282             this.maskEl.left.hide();
10283
10284             this.maskEl.bottom.setStyle('position', 'absolute');
10285             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10286             this.maskEl.bottom.hide();
10287
10288             this.maskEl.right.setStyle('position', 'absolute');
10289             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10290             this.maskEl.right.hide();
10291             
10292             this.toolTip.hide();
10293             
10294             this.toolTip.el.hide();
10295             
10296             window.onwheel = function(){ return true;};
10297             
10298             if(this.intervalID){
10299                 window.clearInterval(this.intervalID);
10300                 this.intervalID = false;
10301             }
10302             
10303             this.isMasked = false;
10304             
10305         }
10306         
10307     }
10308     
10309 });
10310
10311 /*
10312  * Based on:
10313  * Ext JS Library 1.1.1
10314  * Copyright(c) 2006-2007, Ext JS, LLC.
10315  *
10316  * Originally Released Under LGPL - original licence link has changed is not relivant.
10317  *
10318  * Fork - LGPL
10319  * <script type="text/javascript">
10320  */
10321 /**
10322  * @class Roo.form.VTypes
10323  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10324  * @singleton
10325  */
10326 Roo.form.VTypes = function(){
10327     // closure these in so they are only created once.
10328     var alpha = /^[a-zA-Z_]+$/;
10329     var alphanum = /^[a-zA-Z0-9_]+$/;
10330     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10331     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10332
10333     // All these messages and functions are configurable
10334     return {
10335         /**
10336          * The function used to validate email addresses
10337          * @param {String} value The email address
10338          */
10339         'email' : function(v){
10340             return email.test(v);
10341         },
10342         /**
10343          * The error text to display when the email validation function returns false
10344          * @type String
10345          */
10346         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10347         /**
10348          * The keystroke filter mask to be applied on email input
10349          * @type RegExp
10350          */
10351         'emailMask' : /[a-z0-9_\.\-@]/i,
10352
10353         /**
10354          * The function used to validate URLs
10355          * @param {String} value The URL
10356          */
10357         'url' : function(v){
10358             return url.test(v);
10359         },
10360         /**
10361          * The error text to display when the url validation function returns false
10362          * @type String
10363          */
10364         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10365         
10366         /**
10367          * The function used to validate alpha values
10368          * @param {String} value The value
10369          */
10370         'alpha' : function(v){
10371             return alpha.test(v);
10372         },
10373         /**
10374          * The error text to display when the alpha validation function returns false
10375          * @type String
10376          */
10377         'alphaText' : 'This field should only contain letters and _',
10378         /**
10379          * The keystroke filter mask to be applied on alpha input
10380          * @type RegExp
10381          */
10382         'alphaMask' : /[a-z_]/i,
10383
10384         /**
10385          * The function used to validate alphanumeric values
10386          * @param {String} value The value
10387          */
10388         'alphanum' : function(v){
10389             return alphanum.test(v);
10390         },
10391         /**
10392          * The error text to display when the alphanumeric validation function returns false
10393          * @type String
10394          */
10395         'alphanumText' : 'This field should only contain letters, numbers and _',
10396         /**
10397          * The keystroke filter mask to be applied on alphanumeric input
10398          * @type RegExp
10399          */
10400         'alphanumMask' : /[a-z0-9_]/i
10401     };
10402 }();/*
10403  * - LGPL
10404  *
10405  * Input
10406  * 
10407  */
10408
10409 /**
10410  * @class Roo.bootstrap.Input
10411  * @extends Roo.bootstrap.Component
10412  * Bootstrap Input class
10413  * @cfg {Boolean} disabled is it disabled
10414  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10415  * @cfg {String} name name of the input
10416  * @cfg {string} fieldLabel - the label associated
10417  * @cfg {string} placeholder - placeholder to put in text.
10418  * @cfg {string}  before - input group add on before
10419  * @cfg {string} after - input group add on after
10420  * @cfg {string} size - (lg|sm) or leave empty..
10421  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10422  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10423  * @cfg {Number} md colspan out of 12 for computer-sized screens
10424  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10425  * @cfg {string} value default value of the input
10426  * @cfg {Number} labelWidth set the width of label 
10427  * @cfg {Number} labellg set the width of label (1-12)
10428  * @cfg {Number} labelmd set the width of label (1-12)
10429  * @cfg {Number} labelsm set the width of label (1-12)
10430  * @cfg {Number} labelxs set the width of label (1-12)
10431  * @cfg {String} labelAlign (top|left)
10432  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10433  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10434  * @cfg {String} indicatorpos (left|right) default left
10435  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10436  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10437  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10438
10439  * @cfg {String} align (left|center|right) Default left
10440  * @cfg {Boolean} forceFeedback (true|false) Default false
10441  * 
10442  * @constructor
10443  * Create a new Input
10444  * @param {Object} config The config object
10445  */
10446
10447 Roo.bootstrap.Input = function(config){
10448     
10449     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10450     
10451     this.addEvents({
10452         /**
10453          * @event focus
10454          * Fires when this field receives input focus.
10455          * @param {Roo.form.Field} this
10456          */
10457         focus : true,
10458         /**
10459          * @event blur
10460          * Fires when this field loses input focus.
10461          * @param {Roo.form.Field} this
10462          */
10463         blur : true,
10464         /**
10465          * @event specialkey
10466          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10467          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10468          * @param {Roo.form.Field} this
10469          * @param {Roo.EventObject} e The event object
10470          */
10471         specialkey : true,
10472         /**
10473          * @event change
10474          * Fires just before the field blurs if the field value has changed.
10475          * @param {Roo.form.Field} this
10476          * @param {Mixed} newValue The new value
10477          * @param {Mixed} oldValue The original value
10478          */
10479         change : true,
10480         /**
10481          * @event invalid
10482          * Fires after the field has been marked as invalid.
10483          * @param {Roo.form.Field} this
10484          * @param {String} msg The validation message
10485          */
10486         invalid : true,
10487         /**
10488          * @event valid
10489          * Fires after the field has been validated with no errors.
10490          * @param {Roo.form.Field} this
10491          */
10492         valid : true,
10493          /**
10494          * @event keyup
10495          * Fires after the key up
10496          * @param {Roo.form.Field} this
10497          * @param {Roo.EventObject}  e The event Object
10498          */
10499         keyup : true
10500     });
10501 };
10502
10503 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10504      /**
10505      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10506       automatic validation (defaults to "keyup").
10507      */
10508     validationEvent : "keyup",
10509      /**
10510      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10511      */
10512     validateOnBlur : true,
10513     /**
10514      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10515      */
10516     validationDelay : 250,
10517      /**
10518      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10519      */
10520     focusClass : "x-form-focus",  // not needed???
10521     
10522        
10523     /**
10524      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10525      */
10526     invalidClass : "has-warning",
10527     
10528     /**
10529      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10530      */
10531     validClass : "has-success",
10532     
10533     /**
10534      * @cfg {Boolean} hasFeedback (true|false) default true
10535      */
10536     hasFeedback : true,
10537     
10538     /**
10539      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10540      */
10541     invalidFeedbackClass : "glyphicon-warning-sign",
10542     
10543     /**
10544      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10545      */
10546     validFeedbackClass : "glyphicon-ok",
10547     
10548     /**
10549      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10550      */
10551     selectOnFocus : false,
10552     
10553      /**
10554      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10555      */
10556     maskRe : null,
10557        /**
10558      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10559      */
10560     vtype : null,
10561     
10562       /**
10563      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10564      */
10565     disableKeyFilter : false,
10566     
10567        /**
10568      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10569      */
10570     disabled : false,
10571      /**
10572      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10573      */
10574     allowBlank : true,
10575     /**
10576      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10577      */
10578     blankText : "Please complete this mandatory field",
10579     
10580      /**
10581      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10582      */
10583     minLength : 0,
10584     /**
10585      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10586      */
10587     maxLength : Number.MAX_VALUE,
10588     /**
10589      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10590      */
10591     minLengthText : "The minimum length for this field is {0}",
10592     /**
10593      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10594      */
10595     maxLengthText : "The maximum length for this field is {0}",
10596   
10597     
10598     /**
10599      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10600      * If available, this function will be called only after the basic validators all return true, and will be passed the
10601      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10602      */
10603     validator : null,
10604     /**
10605      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10606      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10607      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10608      */
10609     regex : null,
10610     /**
10611      * @cfg {String} regexText -- Depricated - use Invalid Text
10612      */
10613     regexText : "",
10614     
10615     /**
10616      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10617      */
10618     invalidText : "",
10619     
10620     
10621     
10622     autocomplete: false,
10623     
10624     
10625     fieldLabel : '',
10626     inputType : 'text',
10627     
10628     name : false,
10629     placeholder: false,
10630     before : false,
10631     after : false,
10632     size : false,
10633     hasFocus : false,
10634     preventMark: false,
10635     isFormField : true,
10636     value : '',
10637     labelWidth : 2,
10638     labelAlign : false,
10639     readOnly : false,
10640     align : false,
10641     formatedValue : false,
10642     forceFeedback : false,
10643     
10644     indicatorpos : 'left',
10645     
10646     labellg : 0,
10647     labelmd : 0,
10648     labelsm : 0,
10649     labelxs : 0,
10650     
10651     capture : '',
10652     accept : '',
10653     
10654     parentLabelAlign : function()
10655     {
10656         var parent = this;
10657         while (parent.parent()) {
10658             parent = parent.parent();
10659             if (typeof(parent.labelAlign) !='undefined') {
10660                 return parent.labelAlign;
10661             }
10662         }
10663         return 'left';
10664         
10665     },
10666     
10667     getAutoCreate : function()
10668     {
10669         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10670         
10671         var id = Roo.id();
10672         
10673         var cfg = {};
10674         
10675         if(this.inputType != 'hidden'){
10676             cfg.cls = 'form-group' //input-group
10677         }
10678         
10679         var input =  {
10680             tag: 'input',
10681             id : id,
10682             type : this.inputType,
10683             value : this.value,
10684             cls : 'form-control',
10685             placeholder : this.placeholder || '',
10686             autocomplete : this.autocomplete || 'new-password'
10687         };
10688         if (this.inputType == 'file') {
10689             input.style = 'overflow:hidden'; // why not in CSS?
10690         }
10691         
10692         if(this.capture.length){
10693             input.capture = this.capture;
10694         }
10695         
10696         if(this.accept.length){
10697             input.accept = this.accept + "/*";
10698         }
10699         
10700         if(this.align){
10701             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10702         }
10703         
10704         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10705             input.maxLength = this.maxLength;
10706         }
10707         
10708         if (this.disabled) {
10709             input.disabled=true;
10710         }
10711         
10712         if (this.readOnly) {
10713             input.readonly=true;
10714         }
10715         
10716         if (this.name) {
10717             input.name = this.name;
10718         }
10719         
10720         if (this.size) {
10721             input.cls += ' input-' + this.size;
10722         }
10723         
10724         var settings=this;
10725         ['xs','sm','md','lg'].map(function(size){
10726             if (settings[size]) {
10727                 cfg.cls += ' col-' + size + '-' + settings[size];
10728             }
10729         });
10730         
10731         var inputblock = input;
10732         
10733         var feedback = {
10734             tag: 'span',
10735             cls: 'glyphicon form-control-feedback'
10736         };
10737             
10738         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10739             
10740             inputblock = {
10741                 cls : 'has-feedback',
10742                 cn :  [
10743                     input,
10744                     feedback
10745                 ] 
10746             };  
10747         }
10748         
10749         if (this.before || this.after) {
10750             
10751             inputblock = {
10752                 cls : 'input-group',
10753                 cn :  [] 
10754             };
10755             
10756             if (this.before && typeof(this.before) == 'string') {
10757                 
10758                 inputblock.cn.push({
10759                     tag :'span',
10760                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10761                     html : this.before
10762                 });
10763             }
10764             if (this.before && typeof(this.before) == 'object') {
10765                 this.before = Roo.factory(this.before);
10766                 
10767                 inputblock.cn.push({
10768                     tag :'span',
10769                     cls : 'roo-input-before input-group-prepend   input-group-' +
10770                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10771                 });
10772             }
10773             
10774             inputblock.cn.push(input);
10775             
10776             if (this.after && typeof(this.after) == 'string') {
10777                 inputblock.cn.push({
10778                     tag :'span',
10779                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10780                     html : this.after
10781                 });
10782             }
10783             if (this.after && typeof(this.after) == 'object') {
10784                 this.after = Roo.factory(this.after);
10785                 
10786                 inputblock.cn.push({
10787                     tag :'span',
10788                     cls : 'roo-input-after input-group-append  input-group-' +
10789                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10790                 });
10791             }
10792             
10793             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10794                 inputblock.cls += ' has-feedback';
10795                 inputblock.cn.push(feedback);
10796             }
10797         };
10798         var indicator = {
10799             tag : 'i',
10800             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10801             tooltip : 'This field is required'
10802         };
10803         if (this.allowBlank ) {
10804             indicator.style = this.allowBlank ? ' display:none' : '';
10805         }
10806         if (align ==='left' && this.fieldLabel.length) {
10807             
10808             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10809             
10810             cfg.cn = [
10811                 indicator,
10812                 {
10813                     tag: 'label',
10814                     'for' :  id,
10815                     cls : 'control-label col-form-label',
10816                     html : this.fieldLabel
10817
10818                 },
10819                 {
10820                     cls : "", 
10821                     cn: [
10822                         inputblock
10823                     ]
10824                 }
10825             ];
10826             
10827             var labelCfg = cfg.cn[1];
10828             var contentCfg = cfg.cn[2];
10829             
10830             if(this.indicatorpos == 'right'){
10831                 cfg.cn = [
10832                     {
10833                         tag: 'label',
10834                         'for' :  id,
10835                         cls : 'control-label col-form-label',
10836                         cn : [
10837                             {
10838                                 tag : 'span',
10839                                 html : this.fieldLabel
10840                             },
10841                             indicator
10842                         ]
10843                     },
10844                     {
10845                         cls : "",
10846                         cn: [
10847                             inputblock
10848                         ]
10849                     }
10850
10851                 ];
10852                 
10853                 labelCfg = cfg.cn[0];
10854                 contentCfg = cfg.cn[1];
10855             
10856             }
10857             
10858             if(this.labelWidth > 12){
10859                 labelCfg.style = "width: " + this.labelWidth + 'px';
10860             }
10861             
10862             if(this.labelWidth < 13 && this.labelmd == 0){
10863                 this.labelmd = this.labelWidth;
10864             }
10865             
10866             if(this.labellg > 0){
10867                 labelCfg.cls += ' col-lg-' + this.labellg;
10868                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10869             }
10870             
10871             if(this.labelmd > 0){
10872                 labelCfg.cls += ' col-md-' + this.labelmd;
10873                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10874             }
10875             
10876             if(this.labelsm > 0){
10877                 labelCfg.cls += ' col-sm-' + this.labelsm;
10878                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10879             }
10880             
10881             if(this.labelxs > 0){
10882                 labelCfg.cls += ' col-xs-' + this.labelxs;
10883                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10884             }
10885             
10886             
10887         } else if ( this.fieldLabel.length) {
10888                 
10889             
10890             
10891             cfg.cn = [
10892                 {
10893                     tag : 'i',
10894                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10895                     tooltip : 'This field is required',
10896                     style : this.allowBlank ? ' display:none' : '' 
10897                 },
10898                 {
10899                     tag: 'label',
10900                    //cls : 'input-group-addon',
10901                     html : this.fieldLabel
10902
10903                 },
10904
10905                inputblock
10906
10907            ];
10908            
10909            if(this.indicatorpos == 'right'){
10910        
10911                 cfg.cn = [
10912                     {
10913                         tag: 'label',
10914                        //cls : 'input-group-addon',
10915                         html : this.fieldLabel
10916
10917                     },
10918                     {
10919                         tag : 'i',
10920                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10921                         tooltip : 'This field is required',
10922                         style : this.allowBlank ? ' display:none' : '' 
10923                     },
10924
10925                    inputblock
10926
10927                ];
10928
10929             }
10930
10931         } else {
10932             
10933             cfg.cn = [
10934
10935                     inputblock
10936
10937             ];
10938                 
10939                 
10940         };
10941         
10942         if (this.parentType === 'Navbar' &&  this.parent().bar) {
10943            cfg.cls += ' navbar-form';
10944         }
10945         
10946         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10947             // on BS4 we do this only if not form 
10948             cfg.cls += ' navbar-form';
10949             cfg.tag = 'li';
10950         }
10951         
10952         return cfg;
10953         
10954     },
10955     /**
10956      * return the real input element.
10957      */
10958     inputEl: function ()
10959     {
10960         return this.el.select('input.form-control',true).first();
10961     },
10962     
10963     tooltipEl : function()
10964     {
10965         return this.inputEl();
10966     },
10967     
10968     indicatorEl : function()
10969     {
10970         if (Roo.bootstrap.version == 4) {
10971             return false; // not enabled in v4 yet.
10972         }
10973         
10974         var indicator = this.el.select('i.roo-required-indicator',true).first();
10975         
10976         if(!indicator){
10977             return false;
10978         }
10979         
10980         return indicator;
10981         
10982     },
10983     
10984     setDisabled : function(v)
10985     {
10986         var i  = this.inputEl().dom;
10987         if (!v) {
10988             i.removeAttribute('disabled');
10989             return;
10990             
10991         }
10992         i.setAttribute('disabled','true');
10993     },
10994     initEvents : function()
10995     {
10996           
10997         this.inputEl().on("keydown" , this.fireKey,  this);
10998         this.inputEl().on("focus", this.onFocus,  this);
10999         this.inputEl().on("blur", this.onBlur,  this);
11000         
11001         this.inputEl().relayEvent('keyup', this);
11002         
11003         this.indicator = this.indicatorEl();
11004         
11005         if(this.indicator){
11006             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
11007         }
11008  
11009         // reference to original value for reset
11010         this.originalValue = this.getValue();
11011         //Roo.form.TextField.superclass.initEvents.call(this);
11012         if(this.validationEvent == 'keyup'){
11013             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11014             this.inputEl().on('keyup', this.filterValidation, this);
11015         }
11016         else if(this.validationEvent !== false){
11017             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11018         }
11019         
11020         if(this.selectOnFocus){
11021             this.on("focus", this.preFocus, this);
11022             
11023         }
11024         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11025             this.inputEl().on("keypress", this.filterKeys, this);
11026         } else {
11027             this.inputEl().relayEvent('keypress', this);
11028         }
11029        /* if(this.grow){
11030             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11031             this.el.on("click", this.autoSize,  this);
11032         }
11033         */
11034         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11035             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11036         }
11037         
11038         if (typeof(this.before) == 'object') {
11039             this.before.render(this.el.select('.roo-input-before',true).first());
11040         }
11041         if (typeof(this.after) == 'object') {
11042             this.after.render(this.el.select('.roo-input-after',true).first());
11043         }
11044         
11045         this.inputEl().on('change', this.onChange, this);
11046         
11047     },
11048     filterValidation : function(e){
11049         if(!e.isNavKeyPress()){
11050             this.validationTask.delay(this.validationDelay);
11051         }
11052     },
11053      /**
11054      * Validates the field value
11055      * @return {Boolean} True if the value is valid, else false
11056      */
11057     validate : function(){
11058         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11059         if(this.disabled || this.validateValue(this.getRawValue())){
11060             this.markValid();
11061             return true;
11062         }
11063         
11064         this.markInvalid();
11065         return false;
11066     },
11067     
11068     
11069     /**
11070      * Validates a value according to the field's validation rules and marks the field as invalid
11071      * if the validation fails
11072      * @param {Mixed} value The value to validate
11073      * @return {Boolean} True if the value is valid, else false
11074      */
11075     validateValue : function(value)
11076     {
11077         if(this.getVisibilityEl().hasClass('hidden')){
11078             return true;
11079         }
11080         
11081         if(value.length < 1)  { // if it's blank
11082             if(this.allowBlank){
11083                 return true;
11084             }
11085             return false;
11086         }
11087         
11088         if(value.length < this.minLength){
11089             return false;
11090         }
11091         if(value.length > this.maxLength){
11092             return false;
11093         }
11094         if(this.vtype){
11095             var vt = Roo.form.VTypes;
11096             if(!vt[this.vtype](value, this)){
11097                 return false;
11098             }
11099         }
11100         if(typeof this.validator == "function"){
11101             var msg = this.validator(value);
11102             if(msg !== true){
11103                 return false;
11104             }
11105             if (typeof(msg) == 'string') {
11106                 this.invalidText = msg;
11107             }
11108         }
11109         
11110         if(this.regex && !this.regex.test(value)){
11111             return false;
11112         }
11113         
11114         return true;
11115     },
11116     
11117      // private
11118     fireKey : function(e){
11119         //Roo.log('field ' + e.getKey());
11120         if(e.isNavKeyPress()){
11121             this.fireEvent("specialkey", this, e);
11122         }
11123     },
11124     focus : function (selectText){
11125         if(this.rendered){
11126             this.inputEl().focus();
11127             if(selectText === true){
11128                 this.inputEl().dom.select();
11129             }
11130         }
11131         return this;
11132     } ,
11133     
11134     onFocus : function(){
11135         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11136            // this.el.addClass(this.focusClass);
11137         }
11138         if(!this.hasFocus){
11139             this.hasFocus = true;
11140             this.startValue = this.getValue();
11141             this.fireEvent("focus", this);
11142         }
11143     },
11144     
11145     beforeBlur : Roo.emptyFn,
11146
11147     
11148     // private
11149     onBlur : function(){
11150         this.beforeBlur();
11151         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11152             //this.el.removeClass(this.focusClass);
11153         }
11154         this.hasFocus = false;
11155         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11156             this.validate();
11157         }
11158         var v = this.getValue();
11159         if(String(v) !== String(this.startValue)){
11160             this.fireEvent('change', this, v, this.startValue);
11161         }
11162         this.fireEvent("blur", this);
11163     },
11164     
11165     onChange : function(e)
11166     {
11167         var v = this.getValue();
11168         if(String(v) !== String(this.startValue)){
11169             this.fireEvent('change', this, v, this.startValue);
11170         }
11171         
11172     },
11173     
11174     /**
11175      * Resets the current field value to the originally loaded value and clears any validation messages
11176      */
11177     reset : function(){
11178         this.setValue(this.originalValue);
11179         this.validate();
11180     },
11181      /**
11182      * Returns the name of the field
11183      * @return {Mixed} name The name field
11184      */
11185     getName: function(){
11186         return this.name;
11187     },
11188      /**
11189      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11190      * @return {Mixed} value The field value
11191      */
11192     getValue : function(){
11193         
11194         var v = this.inputEl().getValue();
11195         
11196         return v;
11197     },
11198     /**
11199      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11200      * @return {Mixed} value The field value
11201      */
11202     getRawValue : function(){
11203         var v = this.inputEl().getValue();
11204         
11205         return v;
11206     },
11207     
11208     /**
11209      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11210      * @param {Mixed} value The value to set
11211      */
11212     setRawValue : function(v){
11213         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11214     },
11215     
11216     selectText : function(start, end){
11217         var v = this.getRawValue();
11218         if(v.length > 0){
11219             start = start === undefined ? 0 : start;
11220             end = end === undefined ? v.length : end;
11221             var d = this.inputEl().dom;
11222             if(d.setSelectionRange){
11223                 d.setSelectionRange(start, end);
11224             }else if(d.createTextRange){
11225                 var range = d.createTextRange();
11226                 range.moveStart("character", start);
11227                 range.moveEnd("character", v.length-end);
11228                 range.select();
11229             }
11230         }
11231     },
11232     
11233     /**
11234      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11235      * @param {Mixed} value The value to set
11236      */
11237     setValue : function(v){
11238         this.value = v;
11239         if(this.rendered){
11240             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11241             this.validate();
11242         }
11243     },
11244     
11245     /*
11246     processValue : function(value){
11247         if(this.stripCharsRe){
11248             var newValue = value.replace(this.stripCharsRe, '');
11249             if(newValue !== value){
11250                 this.setRawValue(newValue);
11251                 return newValue;
11252             }
11253         }
11254         return value;
11255     },
11256   */
11257     preFocus : function(){
11258         
11259         if(this.selectOnFocus){
11260             this.inputEl().dom.select();
11261         }
11262     },
11263     filterKeys : function(e){
11264         var k = e.getKey();
11265         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11266             return;
11267         }
11268         var c = e.getCharCode(), cc = String.fromCharCode(c);
11269         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11270             return;
11271         }
11272         if(!this.maskRe.test(cc)){
11273             e.stopEvent();
11274         }
11275     },
11276      /**
11277      * Clear any invalid styles/messages for this field
11278      */
11279     clearInvalid : function(){
11280         
11281         if(!this.el || this.preventMark){ // not rendered
11282             return;
11283         }
11284         
11285         
11286         this.el.removeClass([this.invalidClass, 'is-invalid']);
11287         
11288         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11289             
11290             var feedback = this.el.select('.form-control-feedback', true).first();
11291             
11292             if(feedback){
11293                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11294             }
11295             
11296         }
11297         
11298         if(this.indicator){
11299             this.indicator.removeClass('visible');
11300             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11301         }
11302         
11303         this.fireEvent('valid', this);
11304     },
11305     
11306      /**
11307      * Mark this field as valid
11308      */
11309     markValid : function()
11310     {
11311         if(!this.el  || this.preventMark){ // not rendered...
11312             return;
11313         }
11314         
11315         this.el.removeClass([this.invalidClass, this.validClass]);
11316         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11317
11318         var feedback = this.el.select('.form-control-feedback', true).first();
11319             
11320         if(feedback){
11321             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11322         }
11323         
11324         if(this.indicator){
11325             this.indicator.removeClass('visible');
11326             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11327         }
11328         
11329         if(this.disabled){
11330             return;
11331         }
11332         
11333            
11334         if(this.allowBlank && !this.getRawValue().length){
11335             return;
11336         }
11337         if (Roo.bootstrap.version == 3) {
11338             this.el.addClass(this.validClass);
11339         } else {
11340             this.inputEl().addClass('is-valid');
11341         }
11342
11343         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11344             
11345             var feedback = this.el.select('.form-control-feedback', true).first();
11346             
11347             if(feedback){
11348                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11349                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11350             }
11351             
11352         }
11353         
11354         this.fireEvent('valid', this);
11355     },
11356     
11357      /**
11358      * Mark this field as invalid
11359      * @param {String} msg The validation message
11360      */
11361     markInvalid : function(msg)
11362     {
11363         if(!this.el  || this.preventMark){ // not rendered
11364             return;
11365         }
11366         
11367         this.el.removeClass([this.invalidClass, this.validClass]);
11368         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11369         
11370         var feedback = this.el.select('.form-control-feedback', true).first();
11371             
11372         if(feedback){
11373             this.el.select('.form-control-feedback', true).first().removeClass(
11374                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11375         }
11376
11377         if(this.disabled){
11378             return;
11379         }
11380         
11381         if(this.allowBlank && !this.getRawValue().length){
11382             return;
11383         }
11384         
11385         if(this.indicator){
11386             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11387             this.indicator.addClass('visible');
11388         }
11389         if (Roo.bootstrap.version == 3) {
11390             this.el.addClass(this.invalidClass);
11391         } else {
11392             this.inputEl().addClass('is-invalid');
11393         }
11394         
11395         
11396         
11397         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11398             
11399             var feedback = this.el.select('.form-control-feedback', true).first();
11400             
11401             if(feedback){
11402                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11403                 
11404                 if(this.getValue().length || this.forceFeedback){
11405                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11406                 }
11407                 
11408             }
11409             
11410         }
11411         
11412         this.fireEvent('invalid', this, msg);
11413     },
11414     // private
11415     SafariOnKeyDown : function(event)
11416     {
11417         // this is a workaround for a password hang bug on chrome/ webkit.
11418         if (this.inputEl().dom.type != 'password') {
11419             return;
11420         }
11421         
11422         var isSelectAll = false;
11423         
11424         if(this.inputEl().dom.selectionEnd > 0){
11425             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11426         }
11427         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11428             event.preventDefault();
11429             this.setValue('');
11430             return;
11431         }
11432         
11433         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11434             
11435             event.preventDefault();
11436             // this is very hacky as keydown always get's upper case.
11437             //
11438             var cc = String.fromCharCode(event.getCharCode());
11439             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11440             
11441         }
11442     },
11443     adjustWidth : function(tag, w){
11444         tag = tag.toLowerCase();
11445         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11446             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11447                 if(tag == 'input'){
11448                     return w + 2;
11449                 }
11450                 if(tag == 'textarea'){
11451                     return w-2;
11452                 }
11453             }else if(Roo.isOpera){
11454                 if(tag == 'input'){
11455                     return w + 2;
11456                 }
11457                 if(tag == 'textarea'){
11458                     return w-2;
11459                 }
11460             }
11461         }
11462         return w;
11463     },
11464     
11465     setFieldLabel : function(v)
11466     {
11467         if(!this.rendered){
11468             return;
11469         }
11470         
11471         if(this.indicatorEl()){
11472             var ar = this.el.select('label > span',true);
11473             
11474             if (ar.elements.length) {
11475                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11476                 this.fieldLabel = v;
11477                 return;
11478             }
11479             
11480             var br = this.el.select('label',true);
11481             
11482             if(br.elements.length) {
11483                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11484                 this.fieldLabel = v;
11485                 return;
11486             }
11487             
11488             Roo.log('Cannot Found any of label > span || label in input');
11489             return;
11490         }
11491         
11492         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11493         this.fieldLabel = v;
11494         
11495         
11496     }
11497 });
11498
11499  
11500 /*
11501  * - LGPL
11502  *
11503  * Input
11504  * 
11505  */
11506
11507 /**
11508  * @class Roo.bootstrap.TextArea
11509  * @extends Roo.bootstrap.Input
11510  * Bootstrap TextArea class
11511  * @cfg {Number} cols Specifies the visible width of a text area
11512  * @cfg {Number} rows Specifies the visible number of lines in a text area
11513  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11514  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11515  * @cfg {string} html text
11516  * 
11517  * @constructor
11518  * Create a new TextArea
11519  * @param {Object} config The config object
11520  */
11521
11522 Roo.bootstrap.TextArea = function(config){
11523     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11524    
11525 };
11526
11527 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11528      
11529     cols : false,
11530     rows : 5,
11531     readOnly : false,
11532     warp : 'soft',
11533     resize : false,
11534     value: false,
11535     html: false,
11536     
11537     getAutoCreate : function(){
11538         
11539         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11540         
11541         var id = Roo.id();
11542         
11543         var cfg = {};
11544         
11545         if(this.inputType != 'hidden'){
11546             cfg.cls = 'form-group' //input-group
11547         }
11548         
11549         var input =  {
11550             tag: 'textarea',
11551             id : id,
11552             warp : this.warp,
11553             rows : this.rows,
11554             value : this.value || '',
11555             html: this.html || '',
11556             cls : 'form-control',
11557             placeholder : this.placeholder || '' 
11558             
11559         };
11560         
11561         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11562             input.maxLength = this.maxLength;
11563         }
11564         
11565         if(this.resize){
11566             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11567         }
11568         
11569         if(this.cols){
11570             input.cols = this.cols;
11571         }
11572         
11573         if (this.readOnly) {
11574             input.readonly = true;
11575         }
11576         
11577         if (this.name) {
11578             input.name = this.name;
11579         }
11580         
11581         if (this.size) {
11582             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11583         }
11584         
11585         var settings=this;
11586         ['xs','sm','md','lg'].map(function(size){
11587             if (settings[size]) {
11588                 cfg.cls += ' col-' + size + '-' + settings[size];
11589             }
11590         });
11591         
11592         var inputblock = input;
11593         
11594         if(this.hasFeedback && !this.allowBlank){
11595             
11596             var feedback = {
11597                 tag: 'span',
11598                 cls: 'glyphicon form-control-feedback'
11599             };
11600
11601             inputblock = {
11602                 cls : 'has-feedback',
11603                 cn :  [
11604                     input,
11605                     feedback
11606                 ] 
11607             };  
11608         }
11609         
11610         
11611         if (this.before || this.after) {
11612             
11613             inputblock = {
11614                 cls : 'input-group',
11615                 cn :  [] 
11616             };
11617             if (this.before) {
11618                 inputblock.cn.push({
11619                     tag :'span',
11620                     cls : 'input-group-addon',
11621                     html : this.before
11622                 });
11623             }
11624             
11625             inputblock.cn.push(input);
11626             
11627             if(this.hasFeedback && !this.allowBlank){
11628                 inputblock.cls += ' has-feedback';
11629                 inputblock.cn.push(feedback);
11630             }
11631             
11632             if (this.after) {
11633                 inputblock.cn.push({
11634                     tag :'span',
11635                     cls : 'input-group-addon',
11636                     html : this.after
11637                 });
11638             }
11639             
11640         }
11641         
11642         if (align ==='left' && this.fieldLabel.length) {
11643             cfg.cn = [
11644                 {
11645                     tag: 'label',
11646                     'for' :  id,
11647                     cls : 'control-label',
11648                     html : this.fieldLabel
11649                 },
11650                 {
11651                     cls : "",
11652                     cn: [
11653                         inputblock
11654                     ]
11655                 }
11656
11657             ];
11658             
11659             if(this.labelWidth > 12){
11660                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11661             }
11662
11663             if(this.labelWidth < 13 && this.labelmd == 0){
11664                 this.labelmd = this.labelWidth;
11665             }
11666
11667             if(this.labellg > 0){
11668                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11669                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11670             }
11671
11672             if(this.labelmd > 0){
11673                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11674                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11675             }
11676
11677             if(this.labelsm > 0){
11678                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11679                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11680             }
11681
11682             if(this.labelxs > 0){
11683                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11684                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11685             }
11686             
11687         } else if ( this.fieldLabel.length) {
11688             cfg.cn = [
11689
11690                {
11691                    tag: 'label',
11692                    //cls : 'input-group-addon',
11693                    html : this.fieldLabel
11694
11695                },
11696
11697                inputblock
11698
11699            ];
11700
11701         } else {
11702
11703             cfg.cn = [
11704
11705                 inputblock
11706
11707             ];
11708                 
11709         }
11710         
11711         if (this.disabled) {
11712             input.disabled=true;
11713         }
11714         
11715         return cfg;
11716         
11717     },
11718     /**
11719      * return the real textarea element.
11720      */
11721     inputEl: function ()
11722     {
11723         return this.el.select('textarea.form-control',true).first();
11724     },
11725     
11726     /**
11727      * Clear any invalid styles/messages for this field
11728      */
11729     clearInvalid : function()
11730     {
11731         
11732         if(!this.el || this.preventMark){ // not rendered
11733             return;
11734         }
11735         
11736         var label = this.el.select('label', true).first();
11737         var icon = this.el.select('i.fa-star', true).first();
11738         
11739         if(label && icon){
11740             icon.remove();
11741         }
11742         this.el.removeClass( this.validClass);
11743         this.inputEl().removeClass('is-invalid');
11744          
11745         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11746             
11747             var feedback = this.el.select('.form-control-feedback', true).first();
11748             
11749             if(feedback){
11750                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11751             }
11752             
11753         }
11754         
11755         this.fireEvent('valid', this);
11756     },
11757     
11758      /**
11759      * Mark this field as valid
11760      */
11761     markValid : function()
11762     {
11763         if(!this.el  || this.preventMark){ // not rendered
11764             return;
11765         }
11766         
11767         this.el.removeClass([this.invalidClass, this.validClass]);
11768         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11769         
11770         var feedback = this.el.select('.form-control-feedback', true).first();
11771             
11772         if(feedback){
11773             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11774         }
11775
11776         if(this.disabled || this.allowBlank){
11777             return;
11778         }
11779         
11780         var label = this.el.select('label', true).first();
11781         var icon = this.el.select('i.fa-star', true).first();
11782         
11783         if(label && icon){
11784             icon.remove();
11785         }
11786         if (Roo.bootstrap.version == 3) {
11787             this.el.addClass(this.validClass);
11788         } else {
11789             this.inputEl().addClass('is-valid');
11790         }
11791         
11792         
11793         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11794             
11795             var feedback = this.el.select('.form-control-feedback', true).first();
11796             
11797             if(feedback){
11798                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11799                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11800             }
11801             
11802         }
11803         
11804         this.fireEvent('valid', this);
11805     },
11806     
11807      /**
11808      * Mark this field as invalid
11809      * @param {String} msg The validation message
11810      */
11811     markInvalid : function(msg)
11812     {
11813         if(!this.el  || this.preventMark){ // not rendered
11814             return;
11815         }
11816         
11817         this.el.removeClass([this.invalidClass, this.validClass]);
11818         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11819         
11820         var feedback = this.el.select('.form-control-feedback', true).first();
11821             
11822         if(feedback){
11823             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11824         }
11825
11826         if(this.disabled || this.allowBlank){
11827             return;
11828         }
11829         
11830         var label = this.el.select('label', true).first();
11831         var icon = this.el.select('i.fa-star', true).first();
11832         
11833         if(!this.getValue().length && label && !icon){
11834             this.el.createChild({
11835                 tag : 'i',
11836                 cls : 'text-danger fa fa-lg fa-star',
11837                 tooltip : 'This field is required',
11838                 style : 'margin-right:5px;'
11839             }, label, true);
11840         }
11841         
11842         if (Roo.bootstrap.version == 3) {
11843             this.el.addClass(this.invalidClass);
11844         } else {
11845             this.inputEl().addClass('is-invalid');
11846         }
11847         
11848         // fixme ... this may be depricated need to test..
11849         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11850             
11851             var feedback = this.el.select('.form-control-feedback', true).first();
11852             
11853             if(feedback){
11854                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11855                 
11856                 if(this.getValue().length || this.forceFeedback){
11857                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11858                 }
11859                 
11860             }
11861             
11862         }
11863         
11864         this.fireEvent('invalid', this, msg);
11865     }
11866 });
11867
11868  
11869 /*
11870  * - LGPL
11871  *
11872  * trigger field - base class for combo..
11873  * 
11874  */
11875  
11876 /**
11877  * @class Roo.bootstrap.TriggerField
11878  * @extends Roo.bootstrap.Input
11879  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11880  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11881  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11882  * for which you can provide a custom implementation.  For example:
11883  * <pre><code>
11884 var trigger = new Roo.bootstrap.TriggerField();
11885 trigger.onTriggerClick = myTriggerFn;
11886 trigger.applyTo('my-field');
11887 </code></pre>
11888  *
11889  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11890  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11891  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
11892  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11893  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11894
11895  * @constructor
11896  * Create a new TriggerField.
11897  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11898  * to the base TextField)
11899  */
11900 Roo.bootstrap.TriggerField = function(config){
11901     this.mimicing = false;
11902     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11903 };
11904
11905 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
11906     /**
11907      * @cfg {String} triggerClass A CSS class to apply to the trigger
11908      */
11909      /**
11910      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11911      */
11912     hideTrigger:false,
11913
11914     /**
11915      * @cfg {Boolean} removable (true|false) special filter default false
11916      */
11917     removable : false,
11918     
11919     /** @cfg {Boolean} grow @hide */
11920     /** @cfg {Number} growMin @hide */
11921     /** @cfg {Number} growMax @hide */
11922
11923     /**
11924      * @hide 
11925      * @method
11926      */
11927     autoSize: Roo.emptyFn,
11928     // private
11929     monitorTab : true,
11930     // private
11931     deferHeight : true,
11932
11933     
11934     actionMode : 'wrap',
11935     
11936     caret : false,
11937     
11938     
11939     getAutoCreate : function(){
11940        
11941         var align = this.labelAlign || this.parentLabelAlign();
11942         
11943         var id = Roo.id();
11944         
11945         var cfg = {
11946             cls: 'form-group' //input-group
11947         };
11948         
11949         
11950         var input =  {
11951             tag: 'input',
11952             id : id,
11953             type : this.inputType,
11954             cls : 'form-control',
11955             autocomplete: 'new-password',
11956             placeholder : this.placeholder || '' 
11957             
11958         };
11959         if (this.name) {
11960             input.name = this.name;
11961         }
11962         if (this.size) {
11963             input.cls += ' input-' + this.size;
11964         }
11965         
11966         if (this.disabled) {
11967             input.disabled=true;
11968         }
11969         
11970         var inputblock = input;
11971         
11972         if(this.hasFeedback && !this.allowBlank){
11973             
11974             var feedback = {
11975                 tag: 'span',
11976                 cls: 'glyphicon form-control-feedback'
11977             };
11978             
11979             if(this.removable && !this.editable  ){
11980                 inputblock = {
11981                     cls : 'has-feedback',
11982                     cn :  [
11983                         inputblock,
11984                         {
11985                             tag: 'button',
11986                             html : 'x',
11987                             cls : 'roo-combo-removable-btn close'
11988                         },
11989                         feedback
11990                     ] 
11991                 };
11992             } else {
11993                 inputblock = {
11994                     cls : 'has-feedback',
11995                     cn :  [
11996                         inputblock,
11997                         feedback
11998                     ] 
11999                 };
12000             }
12001
12002         } else {
12003             if(this.removable && !this.editable ){
12004                 inputblock = {
12005                     cls : 'roo-removable',
12006                     cn :  [
12007                         inputblock,
12008                         {
12009                             tag: 'button',
12010                             html : 'x',
12011                             cls : 'roo-combo-removable-btn close'
12012                         }
12013                     ] 
12014                 };
12015             }
12016         }
12017         
12018         if (this.before || this.after) {
12019             
12020             inputblock = {
12021                 cls : 'input-group',
12022                 cn :  [] 
12023             };
12024             if (this.before) {
12025                 inputblock.cn.push({
12026                     tag :'span',
12027                     cls : 'input-group-addon input-group-prepend input-group-text',
12028                     html : this.before
12029                 });
12030             }
12031             
12032             inputblock.cn.push(input);
12033             
12034             if(this.hasFeedback && !this.allowBlank){
12035                 inputblock.cls += ' has-feedback';
12036                 inputblock.cn.push(feedback);
12037             }
12038             
12039             if (this.after) {
12040                 inputblock.cn.push({
12041                     tag :'span',
12042                     cls : 'input-group-addon input-group-append input-group-text',
12043                     html : this.after
12044                 });
12045             }
12046             
12047         };
12048         
12049       
12050         
12051         var ibwrap = inputblock;
12052         
12053         if(this.multiple){
12054             ibwrap = {
12055                 tag: 'ul',
12056                 cls: 'roo-select2-choices',
12057                 cn:[
12058                     {
12059                         tag: 'li',
12060                         cls: 'roo-select2-search-field',
12061                         cn: [
12062
12063                             inputblock
12064                         ]
12065                     }
12066                 ]
12067             };
12068                 
12069         }
12070         
12071         var combobox = {
12072             cls: 'roo-select2-container input-group',
12073             cn: [
12074                  {
12075                     tag: 'input',
12076                     type : 'hidden',
12077                     cls: 'form-hidden-field'
12078                 },
12079                 ibwrap
12080             ]
12081         };
12082         
12083         if(!this.multiple && this.showToggleBtn){
12084             
12085             var caret = {
12086                         tag: 'span',
12087                         cls: 'caret'
12088              };
12089             if (this.caret != false) {
12090                 caret = {
12091                      tag: 'i',
12092                      cls: 'fa fa-' + this.caret
12093                 };
12094                 
12095             }
12096             
12097             combobox.cn.push({
12098                 tag :'span',
12099                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12100                 cn : [
12101                     Roo.bootstrap.version == 3 ? caret : '',
12102                     {
12103                         tag: 'span',
12104                         cls: 'combobox-clear',
12105                         cn  : [
12106                             {
12107                                 tag : 'i',
12108                                 cls: 'icon-remove'
12109                             }
12110                         ]
12111                     }
12112                 ]
12113
12114             })
12115         }
12116         
12117         if(this.multiple){
12118             combobox.cls += ' roo-select2-container-multi';
12119         }
12120          var indicator = {
12121             tag : 'i',
12122             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12123             tooltip : 'This field is required'
12124         };
12125         if (Roo.bootstrap.version == 4) {
12126             indicator = {
12127                 tag : 'i',
12128                 style : 'display:none'
12129             };
12130         }
12131         
12132         
12133         if (align ==='left' && this.fieldLabel.length) {
12134             
12135             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12136
12137             cfg.cn = [
12138                 indicator,
12139                 {
12140                     tag: 'label',
12141                     'for' :  id,
12142                     cls : 'control-label',
12143                     html : this.fieldLabel
12144
12145                 },
12146                 {
12147                     cls : "", 
12148                     cn: [
12149                         combobox
12150                     ]
12151                 }
12152
12153             ];
12154             
12155             var labelCfg = cfg.cn[1];
12156             var contentCfg = cfg.cn[2];
12157             
12158             if(this.indicatorpos == 'right'){
12159                 cfg.cn = [
12160                     {
12161                         tag: 'label',
12162                         'for' :  id,
12163                         cls : 'control-label',
12164                         cn : [
12165                             {
12166                                 tag : 'span',
12167                                 html : this.fieldLabel
12168                             },
12169                             indicator
12170                         ]
12171                     },
12172                     {
12173                         cls : "", 
12174                         cn: [
12175                             combobox
12176                         ]
12177                     }
12178
12179                 ];
12180                 
12181                 labelCfg = cfg.cn[0];
12182                 contentCfg = cfg.cn[1];
12183             }
12184             
12185             if(this.labelWidth > 12){
12186                 labelCfg.style = "width: " + this.labelWidth + 'px';
12187             }
12188             
12189             if(this.labelWidth < 13 && this.labelmd == 0){
12190                 this.labelmd = this.labelWidth;
12191             }
12192             
12193             if(this.labellg > 0){
12194                 labelCfg.cls += ' col-lg-' + this.labellg;
12195                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12196             }
12197             
12198             if(this.labelmd > 0){
12199                 labelCfg.cls += ' col-md-' + this.labelmd;
12200                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12201             }
12202             
12203             if(this.labelsm > 0){
12204                 labelCfg.cls += ' col-sm-' + this.labelsm;
12205                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12206             }
12207             
12208             if(this.labelxs > 0){
12209                 labelCfg.cls += ' col-xs-' + this.labelxs;
12210                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12211             }
12212             
12213         } else if ( this.fieldLabel.length) {
12214 //                Roo.log(" label");
12215             cfg.cn = [
12216                 indicator,
12217                {
12218                    tag: 'label',
12219                    //cls : 'input-group-addon',
12220                    html : this.fieldLabel
12221
12222                },
12223
12224                combobox
12225
12226             ];
12227             
12228             if(this.indicatorpos == 'right'){
12229                 
12230                 cfg.cn = [
12231                     {
12232                        tag: 'label',
12233                        cn : [
12234                            {
12235                                tag : 'span',
12236                                html : this.fieldLabel
12237                            },
12238                            indicator
12239                        ]
12240
12241                     },
12242                     combobox
12243
12244                 ];
12245
12246             }
12247
12248         } else {
12249             
12250 //                Roo.log(" no label && no align");
12251                 cfg = combobox
12252                      
12253                 
12254         }
12255         
12256         var settings=this;
12257         ['xs','sm','md','lg'].map(function(size){
12258             if (settings[size]) {
12259                 cfg.cls += ' col-' + size + '-' + settings[size];
12260             }
12261         });
12262         
12263         return cfg;
12264         
12265     },
12266     
12267     
12268     
12269     // private
12270     onResize : function(w, h){
12271 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12272 //        if(typeof w == 'number'){
12273 //            var x = w - this.trigger.getWidth();
12274 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12275 //            this.trigger.setStyle('left', x+'px');
12276 //        }
12277     },
12278
12279     // private
12280     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12281
12282     // private
12283     getResizeEl : function(){
12284         return this.inputEl();
12285     },
12286
12287     // private
12288     getPositionEl : function(){
12289         return this.inputEl();
12290     },
12291
12292     // private
12293     alignErrorIcon : function(){
12294         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12295     },
12296
12297     // private
12298     initEvents : function(){
12299         
12300         this.createList();
12301         
12302         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12303         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12304         if(!this.multiple && this.showToggleBtn){
12305             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12306             if(this.hideTrigger){
12307                 this.trigger.setDisplayed(false);
12308             }
12309             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12310         }
12311         
12312         if(this.multiple){
12313             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12314         }
12315         
12316         if(this.removable && !this.editable && !this.tickable){
12317             var close = this.closeTriggerEl();
12318             
12319             if(close){
12320                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12321                 close.on('click', this.removeBtnClick, this, close);
12322             }
12323         }
12324         
12325         //this.trigger.addClassOnOver('x-form-trigger-over');
12326         //this.trigger.addClassOnClick('x-form-trigger-click');
12327         
12328         //if(!this.width){
12329         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12330         //}
12331     },
12332     
12333     closeTriggerEl : function()
12334     {
12335         var close = this.el.select('.roo-combo-removable-btn', true).first();
12336         return close ? close : false;
12337     },
12338     
12339     removeBtnClick : function(e, h, el)
12340     {
12341         e.preventDefault();
12342         
12343         if(this.fireEvent("remove", this) !== false){
12344             this.reset();
12345             this.fireEvent("afterremove", this)
12346         }
12347     },
12348     
12349     createList : function()
12350     {
12351         this.list = Roo.get(document.body).createChild({
12352             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12353             cls: 'typeahead typeahead-long dropdown-menu shadow',
12354             style: 'display:none'
12355         });
12356         
12357         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12358         
12359     },
12360
12361     // private
12362     initTrigger : function(){
12363        
12364     },
12365
12366     // private
12367     onDestroy : function(){
12368         if(this.trigger){
12369             this.trigger.removeAllListeners();
12370           //  this.trigger.remove();
12371         }
12372         //if(this.wrap){
12373         //    this.wrap.remove();
12374         //}
12375         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12376     },
12377
12378     // private
12379     onFocus : function(){
12380         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12381         /*
12382         if(!this.mimicing){
12383             this.wrap.addClass('x-trigger-wrap-focus');
12384             this.mimicing = true;
12385             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12386             if(this.monitorTab){
12387                 this.el.on("keydown", this.checkTab, this);
12388             }
12389         }
12390         */
12391     },
12392
12393     // private
12394     checkTab : function(e){
12395         if(e.getKey() == e.TAB){
12396             this.triggerBlur();
12397         }
12398     },
12399
12400     // private
12401     onBlur : function(){
12402         // do nothing
12403     },
12404
12405     // private
12406     mimicBlur : function(e, t){
12407         /*
12408         if(!this.wrap.contains(t) && this.validateBlur()){
12409             this.triggerBlur();
12410         }
12411         */
12412     },
12413
12414     // private
12415     triggerBlur : function(){
12416         this.mimicing = false;
12417         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12418         if(this.monitorTab){
12419             this.el.un("keydown", this.checkTab, this);
12420         }
12421         //this.wrap.removeClass('x-trigger-wrap-focus');
12422         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12423     },
12424
12425     // private
12426     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12427     validateBlur : function(e, t){
12428         return true;
12429     },
12430
12431     // private
12432     onDisable : function(){
12433         this.inputEl().dom.disabled = true;
12434         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12435         //if(this.wrap){
12436         //    this.wrap.addClass('x-item-disabled');
12437         //}
12438     },
12439
12440     // private
12441     onEnable : function(){
12442         this.inputEl().dom.disabled = false;
12443         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12444         //if(this.wrap){
12445         //    this.el.removeClass('x-item-disabled');
12446         //}
12447     },
12448
12449     // private
12450     onShow : function(){
12451         var ae = this.getActionEl();
12452         
12453         if(ae){
12454             ae.dom.style.display = '';
12455             ae.dom.style.visibility = 'visible';
12456         }
12457     },
12458
12459     // private
12460     
12461     onHide : function(){
12462         var ae = this.getActionEl();
12463         ae.dom.style.display = 'none';
12464     },
12465
12466     /**
12467      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12468      * by an implementing function.
12469      * @method
12470      * @param {EventObject} e
12471      */
12472     onTriggerClick : Roo.emptyFn
12473 });
12474  
12475 /*
12476 * Licence: LGPL
12477 */
12478
12479 /**
12480  * @class Roo.bootstrap.CardUploader
12481  * @extends Roo.bootstrap.Button
12482  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12483  * @cfg {Number} errorTimeout default 3000
12484  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12485  * @cfg {Array}  html The button text.
12486
12487  *
12488  * @constructor
12489  * Create a new CardUploader
12490  * @param {Object} config The config object
12491  */
12492
12493 Roo.bootstrap.CardUploader = function(config){
12494     
12495  
12496     
12497     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12498     
12499     
12500     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12501         return r.data.id
12502      });
12503     
12504      this.addEvents({
12505          // raw events
12506         /**
12507          * @event view
12508          * When a image is clicked on - and needs to display a slideshow or similar..
12509          * @param {Roo.bootstrap.Card} this
12510          * @param {Object} The image information data 
12511          *
12512          */
12513         'view' : true,
12514          /**
12515          * @event download
12516          * When a the download link is clicked
12517          * @param {Roo.bootstrap.Card} this
12518          * @param {Object} The image information data  contains 
12519          */
12520         'download' : true
12521         
12522     });
12523 };
12524 };
12525
12526 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12527     
12528      
12529     errorTimeout : 3000,
12530      
12531     images : false,
12532    
12533     fileCollection : false,
12534     allowBlank : true,
12535     
12536     getAutoCreate : function()
12537     {
12538         
12539         var cfg =  {
12540             cls :'form-group' ,
12541             cn : [
12542                
12543                 {
12544                     tag: 'label',
12545                    //cls : 'input-group-addon',
12546                     html : this.fieldLabel
12547
12548                 },
12549
12550                 {
12551                     tag: 'input',
12552                     type : 'hidden',
12553                     name : this.name,
12554                     value : this.value,
12555                     cls : 'd-none  form-control'
12556                 },
12557                 
12558                 {
12559                     tag: 'input',
12560                     multiple : 'multiple',
12561                     type : 'file',
12562                     cls : 'd-none  roo-card-upload-selector'
12563                 },
12564                 
12565                 {
12566                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12567                 },
12568                 {
12569                     cls : 'card-columns roo-card-uploader-container'
12570                 }
12571
12572             ]
12573         };
12574            
12575          
12576         return cfg;
12577     },
12578     
12579     getChildContainer : function() /// what children are added to.
12580     {
12581         return this.containerEl;
12582     },
12583    
12584     getButtonContainer : function() /// what children are added to.
12585     {
12586         return this.el.select(".roo-card-uploader-button-container").first();
12587     },
12588    
12589     initEvents : function()
12590     {
12591         
12592         Roo.bootstrap.Input.prototype.initEvents.call(this);
12593         
12594         var t = this;
12595         this.addxtype({
12596             xns: Roo.bootstrap,
12597
12598             xtype : 'Button',
12599             container_method : 'getButtonContainer' ,            
12600             html :  this.html, // fix changable?
12601             cls : 'w-100 ',
12602             listeners : {
12603                 'click' : function(btn, e) {
12604                     t.onClick(e);
12605                 }
12606             }
12607         });
12608         
12609         
12610         
12611         
12612         this.urlAPI = (window.createObjectURL && window) || 
12613                                 (window.URL && URL.revokeObjectURL && URL) || 
12614                                 (window.webkitURL && webkitURL);
12615                         
12616          
12617          
12618          
12619         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12620         
12621         this.selectorEl.on('change', this.onFileSelected, this);
12622         if (this.images) {
12623             var t = this;
12624             this.images.forEach(function(img) {
12625                 t.addCard(img)
12626             });
12627             this.images = false;
12628         }
12629         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12630          
12631        
12632     },
12633     
12634    
12635     onClick : function(e)
12636     {
12637         e.preventDefault();
12638          
12639         this.selectorEl.dom.click();
12640          
12641     },
12642     
12643     onFileSelected : function(e)
12644     {
12645         e.preventDefault();
12646         
12647         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12648             return;
12649         }
12650         
12651         Roo.each(this.selectorEl.dom.files, function(file){    
12652             this.addFile(file);
12653         }, this);
12654          
12655     },
12656     
12657       
12658     
12659       
12660     
12661     addFile : function(file)
12662     {
12663            
12664         if(typeof(file) === 'string'){
12665             throw "Add file by name?"; // should not happen
12666             return;
12667         }
12668         
12669         if(!file || !this.urlAPI){
12670             return;
12671         }
12672         
12673         // file;
12674         // file.type;
12675         
12676         var _this = this;
12677         
12678         
12679         var url = _this.urlAPI.createObjectURL( file);
12680            
12681         this.addCard({
12682             id : Roo.bootstrap.CardUploader.ID--,
12683             is_uploaded : false,
12684             src : url,
12685             srcfile : file,
12686             title : file.name,
12687             mimetype : file.type,
12688             preview : false,
12689             is_deleted : 0
12690         });
12691         
12692     },
12693     
12694     /**
12695      * addCard - add an Attachment to the uploader
12696      * @param data - the data about the image to upload
12697      *
12698      * {
12699           id : 123
12700           title : "Title of file",
12701           is_uploaded : false,
12702           src : "http://.....",
12703           srcfile : { the File upload object },
12704           mimetype : file.type,
12705           preview : false,
12706           is_deleted : 0
12707           
12708         }
12709      *
12710      * 
12711     */
12712     
12713     addCard : function (data)
12714     {
12715         // hidden input element?
12716         // if the file is not an image...
12717         //then we need to use something other that and header_image
12718         var t = this;
12719         //   remove.....
12720         var footer = [
12721             {
12722                 xns : Roo.bootstrap,
12723                 xtype : 'CardFooter',
12724                  items: [
12725                     {
12726                         xns : Roo.bootstrap,
12727                         xtype : 'Element',
12728                         cls : 'd-flex',
12729                         items : [
12730                             
12731                             {
12732                                 xns : Roo.bootstrap,
12733                                 xtype : 'Button',
12734                                 html : String.format("<small>{0}</small>", data.title),
12735                                 cls : 'col-10 text-left',
12736                                 size: 'sm',
12737                                 weight: 'link',
12738                                 fa : 'download',
12739                                 listeners : {
12740                                     click : function() {
12741                                      
12742                                         t.fireEvent( "download", t, data );
12743                                     }
12744                                 }
12745                             },
12746                           
12747                             {
12748                                 xns : Roo.bootstrap,
12749                                 xtype : 'Button',
12750                                 style: 'max-height: 28px; ',
12751                                 size : 'sm',
12752                                 weight: 'danger',
12753                                 cls : 'col-2',
12754                                 fa : 'times',
12755                                 listeners : {
12756                                     click : function() {
12757                                         t.removeCard(data.id)
12758                                     }
12759                                 }
12760                             }
12761                         ]
12762                     }
12763                     
12764                 ] 
12765             }
12766             
12767         ];
12768         
12769         var cn = this.addxtype(
12770             {
12771                  
12772                 xns : Roo.bootstrap,
12773                 xtype : 'Card',
12774                 closeable : true,
12775                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12776                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
12777                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
12778                 data : data,
12779                 html : false,
12780                  
12781                 items : footer,
12782                 initEvents : function() {
12783                     Roo.bootstrap.Card.prototype.initEvents.call(this);
12784                     var card = this;
12785                     this.imgEl = this.el.select('.card-img-top').first();
12786                     if (this.imgEl) {
12787                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
12788                         this.imgEl.set({ 'pointer' : 'cursor' });
12789                                   
12790                     }
12791                     this.getCardFooter().addClass('p-1');
12792                     
12793                   
12794                 }
12795                 
12796             }
12797         );
12798         // dont' really need ot update items.
12799         // this.items.push(cn);
12800         this.fileCollection.add(cn);
12801         
12802         if (!data.srcfile) {
12803             this.updateInput();
12804             return;
12805         }
12806             
12807         var _t = this;
12808         var reader = new FileReader();
12809         reader.addEventListener("load", function() {  
12810             data.srcdata =  reader.result;
12811             _t.updateInput();
12812         });
12813         reader.readAsDataURL(data.srcfile);
12814         
12815         
12816         
12817     },
12818     removeCard : function(id)
12819     {
12820         
12821         var card  = this.fileCollection.get(id);
12822         card.data.is_deleted = 1;
12823         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12824         //this.fileCollection.remove(card);
12825         //this.items = this.items.filter(function(e) { return e != card });
12826         // dont' really need ot update items.
12827         card.el.dom.parentNode.removeChild(card.el.dom);
12828         this.updateInput();
12829
12830         
12831     },
12832     reset: function()
12833     {
12834         this.fileCollection.each(function(card) {
12835             if (card.el.dom && card.el.dom.parentNode) {
12836                 card.el.dom.parentNode.removeChild(card.el.dom);
12837             }
12838         });
12839         this.fileCollection.clear();
12840         this.updateInput();
12841     },
12842     
12843     updateInput : function()
12844     {
12845          var data = [];
12846         this.fileCollection.each(function(e) {
12847             data.push(e.data);
12848             
12849         });
12850         this.inputEl().dom.value = JSON.stringify(data);
12851         
12852         
12853         
12854     }
12855     
12856     
12857 });
12858
12859
12860 Roo.bootstrap.CardUploader.ID = -1;/*
12861  * Based on:
12862  * Ext JS Library 1.1.1
12863  * Copyright(c) 2006-2007, Ext JS, LLC.
12864  *
12865  * Originally Released Under LGPL - original licence link has changed is not relivant.
12866  *
12867  * Fork - LGPL
12868  * <script type="text/javascript">
12869  */
12870
12871
12872 /**
12873  * @class Roo.data.SortTypes
12874  * @singleton
12875  * Defines the default sorting (casting?) comparison functions used when sorting data.
12876  */
12877 Roo.data.SortTypes = {
12878     /**
12879      * Default sort that does nothing
12880      * @param {Mixed} s The value being converted
12881      * @return {Mixed} The comparison value
12882      */
12883     none : function(s){
12884         return s;
12885     },
12886     
12887     /**
12888      * The regular expression used to strip tags
12889      * @type {RegExp}
12890      * @property
12891      */
12892     stripTagsRE : /<\/?[^>]+>/gi,
12893     
12894     /**
12895      * Strips all HTML tags to sort on text only
12896      * @param {Mixed} s The value being converted
12897      * @return {String} The comparison value
12898      */
12899     asText : function(s){
12900         return String(s).replace(this.stripTagsRE, "");
12901     },
12902     
12903     /**
12904      * Strips all HTML tags to sort on text only - Case insensitive
12905      * @param {Mixed} s The value being converted
12906      * @return {String} The comparison value
12907      */
12908     asUCText : function(s){
12909         return String(s).toUpperCase().replace(this.stripTagsRE, "");
12910     },
12911     
12912     /**
12913      * Case insensitive string
12914      * @param {Mixed} s The value being converted
12915      * @return {String} The comparison value
12916      */
12917     asUCString : function(s) {
12918         return String(s).toUpperCase();
12919     },
12920     
12921     /**
12922      * Date sorting
12923      * @param {Mixed} s The value being converted
12924      * @return {Number} The comparison value
12925      */
12926     asDate : function(s) {
12927         if(!s){
12928             return 0;
12929         }
12930         if(s instanceof Date){
12931             return s.getTime();
12932         }
12933         return Date.parse(String(s));
12934     },
12935     
12936     /**
12937      * Float sorting
12938      * @param {Mixed} s The value being converted
12939      * @return {Float} The comparison value
12940      */
12941     asFloat : function(s) {
12942         var val = parseFloat(String(s).replace(/,/g, ""));
12943         if(isNaN(val)) {
12944             val = 0;
12945         }
12946         return val;
12947     },
12948     
12949     /**
12950      * Integer sorting
12951      * @param {Mixed} s The value being converted
12952      * @return {Number} The comparison value
12953      */
12954     asInt : function(s) {
12955         var val = parseInt(String(s).replace(/,/g, ""));
12956         if(isNaN(val)) {
12957             val = 0;
12958         }
12959         return val;
12960     }
12961 };/*
12962  * Based on:
12963  * Ext JS Library 1.1.1
12964  * Copyright(c) 2006-2007, Ext JS, LLC.
12965  *
12966  * Originally Released Under LGPL - original licence link has changed is not relivant.
12967  *
12968  * Fork - LGPL
12969  * <script type="text/javascript">
12970  */
12971
12972 /**
12973 * @class Roo.data.Record
12974  * Instances of this class encapsulate both record <em>definition</em> information, and record
12975  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12976  * to access Records cached in an {@link Roo.data.Store} object.<br>
12977  * <p>
12978  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12979  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12980  * objects.<br>
12981  * <p>
12982  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12983  * @constructor
12984  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12985  * {@link #create}. The parameters are the same.
12986  * @param {Array} data An associative Array of data values keyed by the field name.
12987  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12988  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12989  * not specified an integer id is generated.
12990  */
12991 Roo.data.Record = function(data, id){
12992     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12993     this.data = data;
12994 };
12995
12996 /**
12997  * Generate a constructor for a specific record layout.
12998  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12999  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13000  * Each field definition object may contain the following properties: <ul>
13001  * <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,
13002  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13003  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13004  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13005  * is being used, then this is a string containing the javascript expression to reference the data relative to 
13006  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13007  * to the data item relative to the record element. If the mapping expression is the same as the field name,
13008  * this may be omitted.</p></li>
13009  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13010  * <ul><li>auto (Default, implies no conversion)</li>
13011  * <li>string</li>
13012  * <li>int</li>
13013  * <li>float</li>
13014  * <li>boolean</li>
13015  * <li>date</li></ul></p></li>
13016  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13017  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13018  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13019  * by the Reader into an object that will be stored in the Record. It is passed the
13020  * following parameters:<ul>
13021  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13022  * </ul></p></li>
13023  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13024  * </ul>
13025  * <br>usage:<br><pre><code>
13026 var TopicRecord = Roo.data.Record.create(
13027     {name: 'title', mapping: 'topic_title'},
13028     {name: 'author', mapping: 'username'},
13029     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13030     {name: 'lastPost', mapping: 'post_time', type: 'date'},
13031     {name: 'lastPoster', mapping: 'user2'},
13032     {name: 'excerpt', mapping: 'post_text'}
13033 );
13034
13035 var myNewRecord = new TopicRecord({
13036     title: 'Do my job please',
13037     author: 'noobie',
13038     totalPosts: 1,
13039     lastPost: new Date(),
13040     lastPoster: 'Animal',
13041     excerpt: 'No way dude!'
13042 });
13043 myStore.add(myNewRecord);
13044 </code></pre>
13045  * @method create
13046  * @static
13047  */
13048 Roo.data.Record.create = function(o){
13049     var f = function(){
13050         f.superclass.constructor.apply(this, arguments);
13051     };
13052     Roo.extend(f, Roo.data.Record);
13053     var p = f.prototype;
13054     p.fields = new Roo.util.MixedCollection(false, function(field){
13055         return field.name;
13056     });
13057     for(var i = 0, len = o.length; i < len; i++){
13058         p.fields.add(new Roo.data.Field(o[i]));
13059     }
13060     f.getField = function(name){
13061         return p.fields.get(name);  
13062     };
13063     return f;
13064 };
13065
13066 Roo.data.Record.AUTO_ID = 1000;
13067 Roo.data.Record.EDIT = 'edit';
13068 Roo.data.Record.REJECT = 'reject';
13069 Roo.data.Record.COMMIT = 'commit';
13070
13071 Roo.data.Record.prototype = {
13072     /**
13073      * Readonly flag - true if this record has been modified.
13074      * @type Boolean
13075      */
13076     dirty : false,
13077     editing : false,
13078     error: null,
13079     modified: null,
13080
13081     // private
13082     join : function(store){
13083         this.store = store;
13084     },
13085
13086     /**
13087      * Set the named field to the specified value.
13088      * @param {String} name The name of the field to set.
13089      * @param {Object} value The value to set the field to.
13090      */
13091     set : function(name, value){
13092         if(this.data[name] == value){
13093             return;
13094         }
13095         this.dirty = true;
13096         if(!this.modified){
13097             this.modified = {};
13098         }
13099         if(typeof this.modified[name] == 'undefined'){
13100             this.modified[name] = this.data[name];
13101         }
13102         this.data[name] = value;
13103         if(!this.editing && this.store){
13104             this.store.afterEdit(this);
13105         }       
13106     },
13107
13108     /**
13109      * Get the value of the named field.
13110      * @param {String} name The name of the field to get the value of.
13111      * @return {Object} The value of the field.
13112      */
13113     get : function(name){
13114         return this.data[name]; 
13115     },
13116
13117     // private
13118     beginEdit : function(){
13119         this.editing = true;
13120         this.modified = {}; 
13121     },
13122
13123     // private
13124     cancelEdit : function(){
13125         this.editing = false;
13126         delete this.modified;
13127     },
13128
13129     // private
13130     endEdit : function(){
13131         this.editing = false;
13132         if(this.dirty && this.store){
13133             this.store.afterEdit(this);
13134         }
13135     },
13136
13137     /**
13138      * Usually called by the {@link Roo.data.Store} which owns the Record.
13139      * Rejects all changes made to the Record since either creation, or the last commit operation.
13140      * Modified fields are reverted to their original values.
13141      * <p>
13142      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13143      * of reject operations.
13144      */
13145     reject : function(){
13146         var m = this.modified;
13147         for(var n in m){
13148             if(typeof m[n] != "function"){
13149                 this.data[n] = m[n];
13150             }
13151         }
13152         this.dirty = false;
13153         delete this.modified;
13154         this.editing = false;
13155         if(this.store){
13156             this.store.afterReject(this);
13157         }
13158     },
13159
13160     /**
13161      * Usually called by the {@link Roo.data.Store} which owns the Record.
13162      * Commits all changes made to the Record since either creation, or the last commit operation.
13163      * <p>
13164      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13165      * of commit operations.
13166      */
13167     commit : function(){
13168         this.dirty = false;
13169         delete this.modified;
13170         this.editing = false;
13171         if(this.store){
13172             this.store.afterCommit(this);
13173         }
13174     },
13175
13176     // private
13177     hasError : function(){
13178         return this.error != null;
13179     },
13180
13181     // private
13182     clearError : function(){
13183         this.error = null;
13184     },
13185
13186     /**
13187      * Creates a copy of this record.
13188      * @param {String} id (optional) A new record id if you don't want to use this record's id
13189      * @return {Record}
13190      */
13191     copy : function(newId) {
13192         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13193     }
13194 };/*
13195  * Based on:
13196  * Ext JS Library 1.1.1
13197  * Copyright(c) 2006-2007, Ext JS, LLC.
13198  *
13199  * Originally Released Under LGPL - original licence link has changed is not relivant.
13200  *
13201  * Fork - LGPL
13202  * <script type="text/javascript">
13203  */
13204
13205
13206
13207 /**
13208  * @class Roo.data.Store
13209  * @extends Roo.util.Observable
13210  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13211  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13212  * <p>
13213  * 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
13214  * has no knowledge of the format of the data returned by the Proxy.<br>
13215  * <p>
13216  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13217  * instances from the data object. These records are cached and made available through accessor functions.
13218  * @constructor
13219  * Creates a new Store.
13220  * @param {Object} config A config object containing the objects needed for the Store to access data,
13221  * and read the data into Records.
13222  */
13223 Roo.data.Store = function(config){
13224     this.data = new Roo.util.MixedCollection(false);
13225     this.data.getKey = function(o){
13226         return o.id;
13227     };
13228     this.baseParams = {};
13229     // private
13230     this.paramNames = {
13231         "start" : "start",
13232         "limit" : "limit",
13233         "sort" : "sort",
13234         "dir" : "dir",
13235         "multisort" : "_multisort"
13236     };
13237
13238     if(config && config.data){
13239         this.inlineData = config.data;
13240         delete config.data;
13241     }
13242
13243     Roo.apply(this, config);
13244     
13245     if(this.reader){ // reader passed
13246         this.reader = Roo.factory(this.reader, Roo.data);
13247         this.reader.xmodule = this.xmodule || false;
13248         if(!this.recordType){
13249             this.recordType = this.reader.recordType;
13250         }
13251         if(this.reader.onMetaChange){
13252             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13253         }
13254     }
13255
13256     if(this.recordType){
13257         this.fields = this.recordType.prototype.fields;
13258     }
13259     this.modified = [];
13260
13261     this.addEvents({
13262         /**
13263          * @event datachanged
13264          * Fires when the data cache has changed, and a widget which is using this Store
13265          * as a Record cache should refresh its view.
13266          * @param {Store} this
13267          */
13268         datachanged : true,
13269         /**
13270          * @event metachange
13271          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13272          * @param {Store} this
13273          * @param {Object} meta The JSON metadata
13274          */
13275         metachange : true,
13276         /**
13277          * @event add
13278          * Fires when Records have been added to the Store
13279          * @param {Store} this
13280          * @param {Roo.data.Record[]} records The array of Records added
13281          * @param {Number} index The index at which the record(s) were added
13282          */
13283         add : true,
13284         /**
13285          * @event remove
13286          * Fires when a Record has been removed from the Store
13287          * @param {Store} this
13288          * @param {Roo.data.Record} record The Record that was removed
13289          * @param {Number} index The index at which the record was removed
13290          */
13291         remove : true,
13292         /**
13293          * @event update
13294          * Fires when a Record has been updated
13295          * @param {Store} this
13296          * @param {Roo.data.Record} record The Record that was updated
13297          * @param {String} operation The update operation being performed.  Value may be one of:
13298          * <pre><code>
13299  Roo.data.Record.EDIT
13300  Roo.data.Record.REJECT
13301  Roo.data.Record.COMMIT
13302          * </code></pre>
13303          */
13304         update : true,
13305         /**
13306          * @event clear
13307          * Fires when the data cache has been cleared.
13308          * @param {Store} this
13309          */
13310         clear : true,
13311         /**
13312          * @event beforeload
13313          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13314          * the load action will be canceled.
13315          * @param {Store} this
13316          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13317          */
13318         beforeload : true,
13319         /**
13320          * @event beforeloadadd
13321          * Fires after a new set of Records has been loaded.
13322          * @param {Store} this
13323          * @param {Roo.data.Record[]} records The Records that were loaded
13324          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13325          */
13326         beforeloadadd : true,
13327         /**
13328          * @event load
13329          * Fires after a new set of Records has been loaded, before they are added to the store.
13330          * @param {Store} this
13331          * @param {Roo.data.Record[]} records The Records that were loaded
13332          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13333          * @params {Object} return from reader
13334          */
13335         load : true,
13336         /**
13337          * @event loadexception
13338          * Fires if an exception occurs in the Proxy during loading.
13339          * Called with the signature of the Proxy's "loadexception" event.
13340          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13341          * 
13342          * @param {Proxy} 
13343          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13344          * @param {Object} load options 
13345          * @param {Object} jsonData from your request (normally this contains the Exception)
13346          */
13347         loadexception : true
13348     });
13349     
13350     if(this.proxy){
13351         this.proxy = Roo.factory(this.proxy, Roo.data);
13352         this.proxy.xmodule = this.xmodule || false;
13353         this.relayEvents(this.proxy,  ["loadexception"]);
13354     }
13355     this.sortToggle = {};
13356     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13357
13358     Roo.data.Store.superclass.constructor.call(this);
13359
13360     if(this.inlineData){
13361         this.loadData(this.inlineData);
13362         delete this.inlineData;
13363     }
13364 };
13365
13366 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13367      /**
13368     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13369     * without a remote query - used by combo/forms at present.
13370     */
13371     
13372     /**
13373     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13374     */
13375     /**
13376     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13377     */
13378     /**
13379     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13380     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13381     */
13382     /**
13383     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13384     * on any HTTP request
13385     */
13386     /**
13387     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13388     */
13389     /**
13390     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13391     */
13392     multiSort: false,
13393     /**
13394     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13395     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13396     */
13397     remoteSort : false,
13398
13399     /**
13400     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13401      * loaded or when a record is removed. (defaults to false).
13402     */
13403     pruneModifiedRecords : false,
13404
13405     // private
13406     lastOptions : null,
13407
13408     /**
13409      * Add Records to the Store and fires the add event.
13410      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13411      */
13412     add : function(records){
13413         records = [].concat(records);
13414         for(var i = 0, len = records.length; i < len; i++){
13415             records[i].join(this);
13416         }
13417         var index = this.data.length;
13418         this.data.addAll(records);
13419         this.fireEvent("add", this, records, index);
13420     },
13421
13422     /**
13423      * Remove a Record from the Store and fires the remove event.
13424      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13425      */
13426     remove : function(record){
13427         var index = this.data.indexOf(record);
13428         this.data.removeAt(index);
13429  
13430         if(this.pruneModifiedRecords){
13431             this.modified.remove(record);
13432         }
13433         this.fireEvent("remove", this, record, index);
13434     },
13435
13436     /**
13437      * Remove all Records from the Store and fires the clear event.
13438      */
13439     removeAll : function(){
13440         this.data.clear();
13441         if(this.pruneModifiedRecords){
13442             this.modified = [];
13443         }
13444         this.fireEvent("clear", this);
13445     },
13446
13447     /**
13448      * Inserts Records to the Store at the given index and fires the add event.
13449      * @param {Number} index The start index at which to insert the passed Records.
13450      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13451      */
13452     insert : function(index, records){
13453         records = [].concat(records);
13454         for(var i = 0, len = records.length; i < len; i++){
13455             this.data.insert(index, records[i]);
13456             records[i].join(this);
13457         }
13458         this.fireEvent("add", this, records, index);
13459     },
13460
13461     /**
13462      * Get the index within the cache of the passed Record.
13463      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13464      * @return {Number} The index of the passed Record. Returns -1 if not found.
13465      */
13466     indexOf : function(record){
13467         return this.data.indexOf(record);
13468     },
13469
13470     /**
13471      * Get the index within the cache of the Record with the passed id.
13472      * @param {String} id The id of the Record to find.
13473      * @return {Number} The index of the Record. Returns -1 if not found.
13474      */
13475     indexOfId : function(id){
13476         return this.data.indexOfKey(id);
13477     },
13478
13479     /**
13480      * Get the Record with the specified id.
13481      * @param {String} id The id of the Record to find.
13482      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13483      */
13484     getById : function(id){
13485         return this.data.key(id);
13486     },
13487
13488     /**
13489      * Get the Record at the specified index.
13490      * @param {Number} index The index of the Record to find.
13491      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13492      */
13493     getAt : function(index){
13494         return this.data.itemAt(index);
13495     },
13496
13497     /**
13498      * Returns a range of Records between specified indices.
13499      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13500      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13501      * @return {Roo.data.Record[]} An array of Records
13502      */
13503     getRange : function(start, end){
13504         return this.data.getRange(start, end);
13505     },
13506
13507     // private
13508     storeOptions : function(o){
13509         o = Roo.apply({}, o);
13510         delete o.callback;
13511         delete o.scope;
13512         this.lastOptions = o;
13513     },
13514
13515     /**
13516      * Loads the Record cache from the configured Proxy using the configured Reader.
13517      * <p>
13518      * If using remote paging, then the first load call must specify the <em>start</em>
13519      * and <em>limit</em> properties in the options.params property to establish the initial
13520      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13521      * <p>
13522      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13523      * and this call will return before the new data has been loaded. Perform any post-processing
13524      * in a callback function, or in a "load" event handler.</strong>
13525      * <p>
13526      * @param {Object} options An object containing properties which control loading options:<ul>
13527      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13528      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13529      * passed the following arguments:<ul>
13530      * <li>r : Roo.data.Record[]</li>
13531      * <li>options: Options object from the load call</li>
13532      * <li>success: Boolean success indicator</li></ul></li>
13533      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13534      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13535      * </ul>
13536      */
13537     load : function(options){
13538         options = options || {};
13539         if(this.fireEvent("beforeload", this, options) !== false){
13540             this.storeOptions(options);
13541             var p = Roo.apply(options.params || {}, this.baseParams);
13542             // if meta was not loaded from remote source.. try requesting it.
13543             if (!this.reader.metaFromRemote) {
13544                 p._requestMeta = 1;
13545             }
13546             if(this.sortInfo && this.remoteSort){
13547                 var pn = this.paramNames;
13548                 p[pn["sort"]] = this.sortInfo.field;
13549                 p[pn["dir"]] = this.sortInfo.direction;
13550             }
13551             if (this.multiSort) {
13552                 var pn = this.paramNames;
13553                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13554             }
13555             
13556             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13557         }
13558     },
13559
13560     /**
13561      * Reloads the Record cache from the configured Proxy using the configured Reader and
13562      * the options from the last load operation performed.
13563      * @param {Object} options (optional) An object containing properties which may override the options
13564      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13565      * the most recently used options are reused).
13566      */
13567     reload : function(options){
13568         this.load(Roo.applyIf(options||{}, this.lastOptions));
13569     },
13570
13571     // private
13572     // Called as a callback by the Reader during a load operation.
13573     loadRecords : function(o, options, success){
13574         if(!o || success === false){
13575             if(success !== false){
13576                 this.fireEvent("load", this, [], options, o);
13577             }
13578             if(options.callback){
13579                 options.callback.call(options.scope || this, [], options, false);
13580             }
13581             return;
13582         }
13583         // if data returned failure - throw an exception.
13584         if (o.success === false) {
13585             // show a message if no listener is registered.
13586             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13587                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13588             }
13589             // loadmask wil be hooked into this..
13590             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13591             return;
13592         }
13593         var r = o.records, t = o.totalRecords || r.length;
13594         
13595         this.fireEvent("beforeloadadd", this, r, options, o);
13596         
13597         if(!options || options.add !== true){
13598             if(this.pruneModifiedRecords){
13599                 this.modified = [];
13600             }
13601             for(var i = 0, len = r.length; i < len; i++){
13602                 r[i].join(this);
13603             }
13604             if(this.snapshot){
13605                 this.data = this.snapshot;
13606                 delete this.snapshot;
13607             }
13608             this.data.clear();
13609             this.data.addAll(r);
13610             this.totalLength = t;
13611             this.applySort();
13612             this.fireEvent("datachanged", this);
13613         }else{
13614             this.totalLength = Math.max(t, this.data.length+r.length);
13615             this.add(r);
13616         }
13617         
13618         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13619                 
13620             var e = new Roo.data.Record({});
13621
13622             e.set(this.parent.displayField, this.parent.emptyTitle);
13623             e.set(this.parent.valueField, '');
13624
13625             this.insert(0, e);
13626         }
13627             
13628         this.fireEvent("load", this, r, options, o);
13629         if(options.callback){
13630             options.callback.call(options.scope || this, r, options, true);
13631         }
13632     },
13633
13634
13635     /**
13636      * Loads data from a passed data block. A Reader which understands the format of the data
13637      * must have been configured in the constructor.
13638      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13639      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13640      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13641      */
13642     loadData : function(o, append){
13643         var r = this.reader.readRecords(o);
13644         this.loadRecords(r, {add: append}, true);
13645     },
13646     
13647      /**
13648      * using 'cn' the nested child reader read the child array into it's child stores.
13649      * @param {Object} rec The record with a 'children array
13650      */
13651     loadDataFromChildren : function(rec)
13652     {
13653         this.loadData(this.reader.toLoadData(rec));
13654     },
13655     
13656
13657     /**
13658      * Gets the number of cached records.
13659      * <p>
13660      * <em>If using paging, this may not be the total size of the dataset. If the data object
13661      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13662      * the data set size</em>
13663      */
13664     getCount : function(){
13665         return this.data.length || 0;
13666     },
13667
13668     /**
13669      * Gets the total number of records in the dataset as returned by the server.
13670      * <p>
13671      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13672      * the dataset size</em>
13673      */
13674     getTotalCount : function(){
13675         return this.totalLength || 0;
13676     },
13677
13678     /**
13679      * Returns the sort state of the Store as an object with two properties:
13680      * <pre><code>
13681  field {String} The name of the field by which the Records are sorted
13682  direction {String} The sort order, "ASC" or "DESC"
13683      * </code></pre>
13684      */
13685     getSortState : function(){
13686         return this.sortInfo;
13687     },
13688
13689     // private
13690     applySort : function(){
13691         if(this.sortInfo && !this.remoteSort){
13692             var s = this.sortInfo, f = s.field;
13693             var st = this.fields.get(f).sortType;
13694             var fn = function(r1, r2){
13695                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13696                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13697             };
13698             this.data.sort(s.direction, fn);
13699             if(this.snapshot && this.snapshot != this.data){
13700                 this.snapshot.sort(s.direction, fn);
13701             }
13702         }
13703     },
13704
13705     /**
13706      * Sets the default sort column and order to be used by the next load operation.
13707      * @param {String} fieldName The name of the field to sort by.
13708      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13709      */
13710     setDefaultSort : function(field, dir){
13711         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13712     },
13713
13714     /**
13715      * Sort the Records.
13716      * If remote sorting is used, the sort is performed on the server, and the cache is
13717      * reloaded. If local sorting is used, the cache is sorted internally.
13718      * @param {String} fieldName The name of the field to sort by.
13719      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13720      */
13721     sort : function(fieldName, dir){
13722         var f = this.fields.get(fieldName);
13723         if(!dir){
13724             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13725             
13726             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13727                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13728             }else{
13729                 dir = f.sortDir;
13730             }
13731         }
13732         this.sortToggle[f.name] = dir;
13733         this.sortInfo = {field: f.name, direction: dir};
13734         if(!this.remoteSort){
13735             this.applySort();
13736             this.fireEvent("datachanged", this);
13737         }else{
13738             this.load(this.lastOptions);
13739         }
13740     },
13741
13742     /**
13743      * Calls the specified function for each of the Records in the cache.
13744      * @param {Function} fn The function to call. The Record is passed as the first parameter.
13745      * Returning <em>false</em> aborts and exits the iteration.
13746      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13747      */
13748     each : function(fn, scope){
13749         this.data.each(fn, scope);
13750     },
13751
13752     /**
13753      * Gets all records modified since the last commit.  Modified records are persisted across load operations
13754      * (e.g., during paging).
13755      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13756      */
13757     getModifiedRecords : function(){
13758         return this.modified;
13759     },
13760
13761     // private
13762     createFilterFn : function(property, value, anyMatch){
13763         if(!value.exec){ // not a regex
13764             value = String(value);
13765             if(value.length == 0){
13766                 return false;
13767             }
13768             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13769         }
13770         return function(r){
13771             return value.test(r.data[property]);
13772         };
13773     },
13774
13775     /**
13776      * Sums the value of <i>property</i> for each record between start and end and returns the result.
13777      * @param {String} property A field on your records
13778      * @param {Number} start The record index to start at (defaults to 0)
13779      * @param {Number} end The last record index to include (defaults to length - 1)
13780      * @return {Number} The sum
13781      */
13782     sum : function(property, start, end){
13783         var rs = this.data.items, v = 0;
13784         start = start || 0;
13785         end = (end || end === 0) ? end : rs.length-1;
13786
13787         for(var i = start; i <= end; i++){
13788             v += (rs[i].data[property] || 0);
13789         }
13790         return v;
13791     },
13792
13793     /**
13794      * Filter the records by a specified property.
13795      * @param {String} field A field on your records
13796      * @param {String/RegExp} value Either a string that the field
13797      * should start with or a RegExp to test against the field
13798      * @param {Boolean} anyMatch True to match any part not just the beginning
13799      */
13800     filter : function(property, value, anyMatch){
13801         var fn = this.createFilterFn(property, value, anyMatch);
13802         return fn ? this.filterBy(fn) : this.clearFilter();
13803     },
13804
13805     /**
13806      * Filter by a function. The specified function will be called with each
13807      * record in this data source. If the function returns true the record is included,
13808      * otherwise it is filtered.
13809      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13810      * @param {Object} scope (optional) The scope of the function (defaults to this)
13811      */
13812     filterBy : function(fn, scope){
13813         this.snapshot = this.snapshot || this.data;
13814         this.data = this.queryBy(fn, scope||this);
13815         this.fireEvent("datachanged", this);
13816     },
13817
13818     /**
13819      * Query the records by a specified property.
13820      * @param {String} field A field on your records
13821      * @param {String/RegExp} value Either a string that the field
13822      * should start with or a RegExp to test against the field
13823      * @param {Boolean} anyMatch True to match any part not just the beginning
13824      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13825      */
13826     query : function(property, value, anyMatch){
13827         var fn = this.createFilterFn(property, value, anyMatch);
13828         return fn ? this.queryBy(fn) : this.data.clone();
13829     },
13830
13831     /**
13832      * Query by a function. The specified function will be called with each
13833      * record in this data source. If the function returns true the record is included
13834      * in the results.
13835      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13836      * @param {Object} scope (optional) The scope of the function (defaults to this)
13837       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13838      **/
13839     queryBy : function(fn, scope){
13840         var data = this.snapshot || this.data;
13841         return data.filterBy(fn, scope||this);
13842     },
13843
13844     /**
13845      * Collects unique values for a particular dataIndex from this store.
13846      * @param {String} dataIndex The property to collect
13847      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13848      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13849      * @return {Array} An array of the unique values
13850      **/
13851     collect : function(dataIndex, allowNull, bypassFilter){
13852         var d = (bypassFilter === true && this.snapshot) ?
13853                 this.snapshot.items : this.data.items;
13854         var v, sv, r = [], l = {};
13855         for(var i = 0, len = d.length; i < len; i++){
13856             v = d[i].data[dataIndex];
13857             sv = String(v);
13858             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13859                 l[sv] = true;
13860                 r[r.length] = v;
13861             }
13862         }
13863         return r;
13864     },
13865
13866     /**
13867      * Revert to a view of the Record cache with no filtering applied.
13868      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13869      */
13870     clearFilter : function(suppressEvent){
13871         if(this.snapshot && this.snapshot != this.data){
13872             this.data = this.snapshot;
13873             delete this.snapshot;
13874             if(suppressEvent !== true){
13875                 this.fireEvent("datachanged", this);
13876             }
13877         }
13878     },
13879
13880     // private
13881     afterEdit : function(record){
13882         if(this.modified.indexOf(record) == -1){
13883             this.modified.push(record);
13884         }
13885         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13886     },
13887     
13888     // private
13889     afterReject : function(record){
13890         this.modified.remove(record);
13891         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13892     },
13893
13894     // private
13895     afterCommit : function(record){
13896         this.modified.remove(record);
13897         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13898     },
13899
13900     /**
13901      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13902      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13903      */
13904     commitChanges : function(){
13905         var m = this.modified.slice(0);
13906         this.modified = [];
13907         for(var i = 0, len = m.length; i < len; i++){
13908             m[i].commit();
13909         }
13910     },
13911
13912     /**
13913      * Cancel outstanding changes on all changed records.
13914      */
13915     rejectChanges : function(){
13916         var m = this.modified.slice(0);
13917         this.modified = [];
13918         for(var i = 0, len = m.length; i < len; i++){
13919             m[i].reject();
13920         }
13921     },
13922
13923     onMetaChange : function(meta, rtype, o){
13924         this.recordType = rtype;
13925         this.fields = rtype.prototype.fields;
13926         delete this.snapshot;
13927         this.sortInfo = meta.sortInfo || this.sortInfo;
13928         this.modified = [];
13929         this.fireEvent('metachange', this, this.reader.meta);
13930     },
13931     
13932     moveIndex : function(data, type)
13933     {
13934         var index = this.indexOf(data);
13935         
13936         var newIndex = index + type;
13937         
13938         this.remove(data);
13939         
13940         this.insert(newIndex, data);
13941         
13942     }
13943 });/*
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  * @class Roo.data.SimpleStore
13956  * @extends Roo.data.Store
13957  * Small helper class to make creating Stores from Array data easier.
13958  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13959  * @cfg {Array} fields An array of field definition objects, or field name strings.
13960  * @cfg {Object} an existing reader (eg. copied from another store)
13961  * @cfg {Array} data The multi-dimensional array of data
13962  * @constructor
13963  * @param {Object} config
13964  */
13965 Roo.data.SimpleStore = function(config)
13966 {
13967     Roo.data.SimpleStore.superclass.constructor.call(this, {
13968         isLocal : true,
13969         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13970                 id: config.id
13971             },
13972             Roo.data.Record.create(config.fields)
13973         ),
13974         proxy : new Roo.data.MemoryProxy(config.data)
13975     });
13976     this.load();
13977 };
13978 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13979  * Based on:
13980  * Ext JS Library 1.1.1
13981  * Copyright(c) 2006-2007, Ext JS, LLC.
13982  *
13983  * Originally Released Under LGPL - original licence link has changed is not relivant.
13984  *
13985  * Fork - LGPL
13986  * <script type="text/javascript">
13987  */
13988
13989 /**
13990 /**
13991  * @extends Roo.data.Store
13992  * @class Roo.data.JsonStore
13993  * Small helper class to make creating Stores for JSON data easier. <br/>
13994 <pre><code>
13995 var store = new Roo.data.JsonStore({
13996     url: 'get-images.php',
13997     root: 'images',
13998     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13999 });
14000 </code></pre>
14001  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14002  * JsonReader and HttpProxy (unless inline data is provided).</b>
14003  * @cfg {Array} fields An array of field definition objects, or field name strings.
14004  * @constructor
14005  * @param {Object} config
14006  */
14007 Roo.data.JsonStore = function(c){
14008     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14009         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14010         reader: new Roo.data.JsonReader(c, c.fields)
14011     }));
14012 };
14013 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14014  * Based on:
14015  * Ext JS Library 1.1.1
14016  * Copyright(c) 2006-2007, Ext JS, LLC.
14017  *
14018  * Originally Released Under LGPL - original licence link has changed is not relivant.
14019  *
14020  * Fork - LGPL
14021  * <script type="text/javascript">
14022  */
14023
14024  
14025 Roo.data.Field = function(config){
14026     if(typeof config == "string"){
14027         config = {name: config};
14028     }
14029     Roo.apply(this, config);
14030     
14031     if(!this.type){
14032         this.type = "auto";
14033     }
14034     
14035     var st = Roo.data.SortTypes;
14036     // named sortTypes are supported, here we look them up
14037     if(typeof this.sortType == "string"){
14038         this.sortType = st[this.sortType];
14039     }
14040     
14041     // set default sortType for strings and dates
14042     if(!this.sortType){
14043         switch(this.type){
14044             case "string":
14045                 this.sortType = st.asUCString;
14046                 break;
14047             case "date":
14048                 this.sortType = st.asDate;
14049                 break;
14050             default:
14051                 this.sortType = st.none;
14052         }
14053     }
14054
14055     // define once
14056     var stripRe = /[\$,%]/g;
14057
14058     // prebuilt conversion function for this field, instead of
14059     // switching every time we're reading a value
14060     if(!this.convert){
14061         var cv, dateFormat = this.dateFormat;
14062         switch(this.type){
14063             case "":
14064             case "auto":
14065             case undefined:
14066                 cv = function(v){ return v; };
14067                 break;
14068             case "string":
14069                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14070                 break;
14071             case "int":
14072                 cv = function(v){
14073                     return v !== undefined && v !== null && v !== '' ?
14074                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14075                     };
14076                 break;
14077             case "float":
14078                 cv = function(v){
14079                     return v !== undefined && v !== null && v !== '' ?
14080                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14081                     };
14082                 break;
14083             case "bool":
14084             case "boolean":
14085                 cv = function(v){ return v === true || v === "true" || v == 1; };
14086                 break;
14087             case "date":
14088                 cv = function(v){
14089                     if(!v){
14090                         return '';
14091                     }
14092                     if(v instanceof Date){
14093                         return v;
14094                     }
14095                     if(dateFormat){
14096                         if(dateFormat == "timestamp"){
14097                             return new Date(v*1000);
14098                         }
14099                         return Date.parseDate(v, dateFormat);
14100                     }
14101                     var parsed = Date.parse(v);
14102                     return parsed ? new Date(parsed) : null;
14103                 };
14104              break;
14105             
14106         }
14107         this.convert = cv;
14108     }
14109 };
14110
14111 Roo.data.Field.prototype = {
14112     dateFormat: null,
14113     defaultValue: "",
14114     mapping: null,
14115     sortType : null,
14116     sortDir : "ASC"
14117 };/*
14118  * Based on:
14119  * Ext JS Library 1.1.1
14120  * Copyright(c) 2006-2007, Ext JS, LLC.
14121  *
14122  * Originally Released Under LGPL - original licence link has changed is not relivant.
14123  *
14124  * Fork - LGPL
14125  * <script type="text/javascript">
14126  */
14127  
14128 // Base class for reading structured data from a data source.  This class is intended to be
14129 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14130
14131 /**
14132  * @class Roo.data.DataReader
14133  * Base class for reading structured data from a data source.  This class is intended to be
14134  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14135  */
14136
14137 Roo.data.DataReader = function(meta, recordType){
14138     
14139     this.meta = meta;
14140     
14141     this.recordType = recordType instanceof Array ? 
14142         Roo.data.Record.create(recordType) : recordType;
14143 };
14144
14145 Roo.data.DataReader.prototype = {
14146     
14147     
14148     readerType : 'Data',
14149      /**
14150      * Create an empty record
14151      * @param {Object} data (optional) - overlay some values
14152      * @return {Roo.data.Record} record created.
14153      */
14154     newRow :  function(d) {
14155         var da =  {};
14156         this.recordType.prototype.fields.each(function(c) {
14157             switch( c.type) {
14158                 case 'int' : da[c.name] = 0; break;
14159                 case 'date' : da[c.name] = new Date(); break;
14160                 case 'float' : da[c.name] = 0.0; break;
14161                 case 'boolean' : da[c.name] = false; break;
14162                 default : da[c.name] = ""; break;
14163             }
14164             
14165         });
14166         return new this.recordType(Roo.apply(da, d));
14167     }
14168     
14169     
14170 };/*
14171  * Based on:
14172  * Ext JS Library 1.1.1
14173  * Copyright(c) 2006-2007, Ext JS, LLC.
14174  *
14175  * Originally Released Under LGPL - original licence link has changed is not relivant.
14176  *
14177  * Fork - LGPL
14178  * <script type="text/javascript">
14179  */
14180
14181 /**
14182  * @class Roo.data.DataProxy
14183  * @extends Roo.data.Observable
14184  * This class is an abstract base class for implementations which provide retrieval of
14185  * unformatted data objects.<br>
14186  * <p>
14187  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14188  * (of the appropriate type which knows how to parse the data object) to provide a block of
14189  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14190  * <p>
14191  * Custom implementations must implement the load method as described in
14192  * {@link Roo.data.HttpProxy#load}.
14193  */
14194 Roo.data.DataProxy = function(){
14195     this.addEvents({
14196         /**
14197          * @event beforeload
14198          * Fires before a network request is made to retrieve a data object.
14199          * @param {Object} This DataProxy object.
14200          * @param {Object} params The params parameter to the load function.
14201          */
14202         beforeload : true,
14203         /**
14204          * @event load
14205          * Fires before the load method's callback is called.
14206          * @param {Object} This DataProxy object.
14207          * @param {Object} o The data object.
14208          * @param {Object} arg The callback argument object passed to the load function.
14209          */
14210         load : true,
14211         /**
14212          * @event loadexception
14213          * Fires if an Exception occurs during data retrieval.
14214          * @param {Object} This DataProxy object.
14215          * @param {Object} o The data object.
14216          * @param {Object} arg The callback argument object passed to the load function.
14217          * @param {Object} e The Exception.
14218          */
14219         loadexception : true
14220     });
14221     Roo.data.DataProxy.superclass.constructor.call(this);
14222 };
14223
14224 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14225
14226     /**
14227      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14228      */
14229 /*
14230  * Based on:
14231  * Ext JS Library 1.1.1
14232  * Copyright(c) 2006-2007, Ext JS, LLC.
14233  *
14234  * Originally Released Under LGPL - original licence link has changed is not relivant.
14235  *
14236  * Fork - LGPL
14237  * <script type="text/javascript">
14238  */
14239 /**
14240  * @class Roo.data.MemoryProxy
14241  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14242  * to the Reader when its load method is called.
14243  * @constructor
14244  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14245  */
14246 Roo.data.MemoryProxy = function(data){
14247     if (data.data) {
14248         data = data.data;
14249     }
14250     Roo.data.MemoryProxy.superclass.constructor.call(this);
14251     this.data = data;
14252 };
14253
14254 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14255     
14256     /**
14257      * Load data from the requested source (in this case an in-memory
14258      * data object passed to the constructor), read the data object into
14259      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14260      * process that block using the passed callback.
14261      * @param {Object} params This parameter is not used by the MemoryProxy class.
14262      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14263      * object into a block of Roo.data.Records.
14264      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14265      * The function must be passed <ul>
14266      * <li>The Record block object</li>
14267      * <li>The "arg" argument from the load function</li>
14268      * <li>A boolean success indicator</li>
14269      * </ul>
14270      * @param {Object} scope The scope in which to call the callback
14271      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14272      */
14273     load : function(params, reader, callback, scope, arg){
14274         params = params || {};
14275         var result;
14276         try {
14277             result = reader.readRecords(params.data ? params.data :this.data);
14278         }catch(e){
14279             this.fireEvent("loadexception", this, arg, null, e);
14280             callback.call(scope, null, arg, false);
14281             return;
14282         }
14283         callback.call(scope, result, arg, true);
14284     },
14285     
14286     // private
14287     update : function(params, records){
14288         
14289     }
14290 });/*
14291  * Based on:
14292  * Ext JS Library 1.1.1
14293  * Copyright(c) 2006-2007, Ext JS, LLC.
14294  *
14295  * Originally Released Under LGPL - original licence link has changed is not relivant.
14296  *
14297  * Fork - LGPL
14298  * <script type="text/javascript">
14299  */
14300 /**
14301  * @class Roo.data.HttpProxy
14302  * @extends Roo.data.DataProxy
14303  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14304  * configured to reference a certain URL.<br><br>
14305  * <p>
14306  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14307  * from which the running page was served.<br><br>
14308  * <p>
14309  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14310  * <p>
14311  * Be aware that to enable the browser to parse an XML document, the server must set
14312  * the Content-Type header in the HTTP response to "text/xml".
14313  * @constructor
14314  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14315  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14316  * will be used to make the request.
14317  */
14318 Roo.data.HttpProxy = function(conn){
14319     Roo.data.HttpProxy.superclass.constructor.call(this);
14320     // is conn a conn config or a real conn?
14321     this.conn = conn;
14322     this.useAjax = !conn || !conn.events;
14323   
14324 };
14325
14326 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14327     // thse are take from connection...
14328     
14329     /**
14330      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14331      */
14332     /**
14333      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14334      * extra parameters to each request made by this object. (defaults to undefined)
14335      */
14336     /**
14337      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14338      *  to each request made by this object. (defaults to undefined)
14339      */
14340     /**
14341      * @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)
14342      */
14343     /**
14344      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14345      */
14346      /**
14347      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14348      * @type Boolean
14349      */
14350   
14351
14352     /**
14353      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14354      * @type Boolean
14355      */
14356     /**
14357      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14358      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14359      * a finer-grained basis than the DataProxy events.
14360      */
14361     getConnection : function(){
14362         return this.useAjax ? Roo.Ajax : this.conn;
14363     },
14364
14365     /**
14366      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14367      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14368      * process that block using the passed callback.
14369      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14370      * for the request to the remote server.
14371      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14372      * object into a block of Roo.data.Records.
14373      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14374      * The function must be passed <ul>
14375      * <li>The Record block object</li>
14376      * <li>The "arg" argument from the load function</li>
14377      * <li>A boolean success indicator</li>
14378      * </ul>
14379      * @param {Object} scope The scope in which to call the callback
14380      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14381      */
14382     load : function(params, reader, callback, scope, arg){
14383         if(this.fireEvent("beforeload", this, params) !== false){
14384             var  o = {
14385                 params : params || {},
14386                 request: {
14387                     callback : callback,
14388                     scope : scope,
14389                     arg : arg
14390                 },
14391                 reader: reader,
14392                 callback : this.loadResponse,
14393                 scope: this
14394             };
14395             if(this.useAjax){
14396                 Roo.applyIf(o, this.conn);
14397                 if(this.activeRequest){
14398                     Roo.Ajax.abort(this.activeRequest);
14399                 }
14400                 this.activeRequest = Roo.Ajax.request(o);
14401             }else{
14402                 this.conn.request(o);
14403             }
14404         }else{
14405             callback.call(scope||this, null, arg, false);
14406         }
14407     },
14408
14409     // private
14410     loadResponse : function(o, success, response){
14411         delete this.activeRequest;
14412         if(!success){
14413             this.fireEvent("loadexception", this, o, response);
14414             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14415             return;
14416         }
14417         var result;
14418         try {
14419             result = o.reader.read(response);
14420         }catch(e){
14421             this.fireEvent("loadexception", this, o, response, e);
14422             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14423             return;
14424         }
14425         
14426         this.fireEvent("load", this, o, o.request.arg);
14427         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14428     },
14429
14430     // private
14431     update : function(dataSet){
14432
14433     },
14434
14435     // private
14436     updateResponse : function(dataSet){
14437
14438     }
14439 });/*
14440  * Based on:
14441  * Ext JS Library 1.1.1
14442  * Copyright(c) 2006-2007, Ext JS, LLC.
14443  *
14444  * Originally Released Under LGPL - original licence link has changed is not relivant.
14445  *
14446  * Fork - LGPL
14447  * <script type="text/javascript">
14448  */
14449
14450 /**
14451  * @class Roo.data.ScriptTagProxy
14452  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14453  * other than the originating domain of the running page.<br><br>
14454  * <p>
14455  * <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
14456  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14457  * <p>
14458  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14459  * source code that is used as the source inside a &lt;script> tag.<br><br>
14460  * <p>
14461  * In order for the browser to process the returned data, the server must wrap the data object
14462  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14463  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14464  * depending on whether the callback name was passed:
14465  * <p>
14466  * <pre><code>
14467 boolean scriptTag = false;
14468 String cb = request.getParameter("callback");
14469 if (cb != null) {
14470     scriptTag = true;
14471     response.setContentType("text/javascript");
14472 } else {
14473     response.setContentType("application/x-json");
14474 }
14475 Writer out = response.getWriter();
14476 if (scriptTag) {
14477     out.write(cb + "(");
14478 }
14479 out.print(dataBlock.toJsonString());
14480 if (scriptTag) {
14481     out.write(");");
14482 }
14483 </pre></code>
14484  *
14485  * @constructor
14486  * @param {Object} config A configuration object.
14487  */
14488 Roo.data.ScriptTagProxy = function(config){
14489     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14490     Roo.apply(this, config);
14491     this.head = document.getElementsByTagName("head")[0];
14492 };
14493
14494 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14495
14496 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14497     /**
14498      * @cfg {String} url The URL from which to request the data object.
14499      */
14500     /**
14501      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14502      */
14503     timeout : 30000,
14504     /**
14505      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14506      * the server the name of the callback function set up by the load call to process the returned data object.
14507      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14508      * javascript output which calls this named function passing the data object as its only parameter.
14509      */
14510     callbackParam : "callback",
14511     /**
14512      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14513      * name to the request.
14514      */
14515     nocache : true,
14516
14517     /**
14518      * Load data from the configured URL, read the data object into
14519      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14520      * process that block using the passed callback.
14521      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14522      * for the request to the remote server.
14523      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14524      * object into a block of Roo.data.Records.
14525      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14526      * The function must be passed <ul>
14527      * <li>The Record block object</li>
14528      * <li>The "arg" argument from the load function</li>
14529      * <li>A boolean success indicator</li>
14530      * </ul>
14531      * @param {Object} scope The scope in which to call the callback
14532      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14533      */
14534     load : function(params, reader, callback, scope, arg){
14535         if(this.fireEvent("beforeload", this, params) !== false){
14536
14537             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14538
14539             var url = this.url;
14540             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14541             if(this.nocache){
14542                 url += "&_dc=" + (new Date().getTime());
14543             }
14544             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14545             var trans = {
14546                 id : transId,
14547                 cb : "stcCallback"+transId,
14548                 scriptId : "stcScript"+transId,
14549                 params : params,
14550                 arg : arg,
14551                 url : url,
14552                 callback : callback,
14553                 scope : scope,
14554                 reader : reader
14555             };
14556             var conn = this;
14557
14558             window[trans.cb] = function(o){
14559                 conn.handleResponse(o, trans);
14560             };
14561
14562             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14563
14564             if(this.autoAbort !== false){
14565                 this.abort();
14566             }
14567
14568             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14569
14570             var script = document.createElement("script");
14571             script.setAttribute("src", url);
14572             script.setAttribute("type", "text/javascript");
14573             script.setAttribute("id", trans.scriptId);
14574             this.head.appendChild(script);
14575
14576             this.trans = trans;
14577         }else{
14578             callback.call(scope||this, null, arg, false);
14579         }
14580     },
14581
14582     // private
14583     isLoading : function(){
14584         return this.trans ? true : false;
14585     },
14586
14587     /**
14588      * Abort the current server request.
14589      */
14590     abort : function(){
14591         if(this.isLoading()){
14592             this.destroyTrans(this.trans);
14593         }
14594     },
14595
14596     // private
14597     destroyTrans : function(trans, isLoaded){
14598         this.head.removeChild(document.getElementById(trans.scriptId));
14599         clearTimeout(trans.timeoutId);
14600         if(isLoaded){
14601             window[trans.cb] = undefined;
14602             try{
14603                 delete window[trans.cb];
14604             }catch(e){}
14605         }else{
14606             // if hasn't been loaded, wait for load to remove it to prevent script error
14607             window[trans.cb] = function(){
14608                 window[trans.cb] = undefined;
14609                 try{
14610                     delete window[trans.cb];
14611                 }catch(e){}
14612             };
14613         }
14614     },
14615
14616     // private
14617     handleResponse : function(o, trans){
14618         this.trans = false;
14619         this.destroyTrans(trans, true);
14620         var result;
14621         try {
14622             result = trans.reader.readRecords(o);
14623         }catch(e){
14624             this.fireEvent("loadexception", this, o, trans.arg, e);
14625             trans.callback.call(trans.scope||window, null, trans.arg, false);
14626             return;
14627         }
14628         this.fireEvent("load", this, o, trans.arg);
14629         trans.callback.call(trans.scope||window, result, trans.arg, true);
14630     },
14631
14632     // private
14633     handleFailure : function(trans){
14634         this.trans = false;
14635         this.destroyTrans(trans, false);
14636         this.fireEvent("loadexception", this, null, trans.arg);
14637         trans.callback.call(trans.scope||window, null, trans.arg, false);
14638     }
14639 });/*
14640  * Based on:
14641  * Ext JS Library 1.1.1
14642  * Copyright(c) 2006-2007, Ext JS, LLC.
14643  *
14644  * Originally Released Under LGPL - original licence link has changed is not relivant.
14645  *
14646  * Fork - LGPL
14647  * <script type="text/javascript">
14648  */
14649
14650 /**
14651  * @class Roo.data.JsonReader
14652  * @extends Roo.data.DataReader
14653  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14654  * based on mappings in a provided Roo.data.Record constructor.
14655  * 
14656  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14657  * in the reply previously. 
14658  * 
14659  * <p>
14660  * Example code:
14661  * <pre><code>
14662 var RecordDef = Roo.data.Record.create([
14663     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14664     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14665 ]);
14666 var myReader = new Roo.data.JsonReader({
14667     totalProperty: "results",    // The property which contains the total dataset size (optional)
14668     root: "rows",                // The property which contains an Array of row objects
14669     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14670 }, RecordDef);
14671 </code></pre>
14672  * <p>
14673  * This would consume a JSON file like this:
14674  * <pre><code>
14675 { 'results': 2, 'rows': [
14676     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14677     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14678 }
14679 </code></pre>
14680  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14681  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14682  * paged from the remote server.
14683  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14684  * @cfg {String} root name of the property which contains the Array of row objects.
14685  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14686  * @cfg {Array} fields Array of field definition objects
14687  * @constructor
14688  * Create a new JsonReader
14689  * @param {Object} meta Metadata configuration options
14690  * @param {Object} recordType Either an Array of field definition objects,
14691  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14692  */
14693 Roo.data.JsonReader = function(meta, recordType){
14694     
14695     meta = meta || {};
14696     // set some defaults:
14697     Roo.applyIf(meta, {
14698         totalProperty: 'total',
14699         successProperty : 'success',
14700         root : 'data',
14701         id : 'id'
14702     });
14703     
14704     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14705 };
14706 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14707     
14708     readerType : 'Json',
14709     
14710     /**
14711      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14712      * Used by Store query builder to append _requestMeta to params.
14713      * 
14714      */
14715     metaFromRemote : false,
14716     /**
14717      * This method is only used by a DataProxy which has retrieved data from a remote server.
14718      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14719      * @return {Object} data A data block which is used by an Roo.data.Store object as
14720      * a cache of Roo.data.Records.
14721      */
14722     read : function(response){
14723         var json = response.responseText;
14724        
14725         var o = /* eval:var:o */ eval("("+json+")");
14726         if(!o) {
14727             throw {message: "JsonReader.read: Json object not found"};
14728         }
14729         
14730         if(o.metaData){
14731             
14732             delete this.ef;
14733             this.metaFromRemote = true;
14734             this.meta = o.metaData;
14735             this.recordType = Roo.data.Record.create(o.metaData.fields);
14736             this.onMetaChange(this.meta, this.recordType, o);
14737         }
14738         return this.readRecords(o);
14739     },
14740
14741     // private function a store will implement
14742     onMetaChange : function(meta, recordType, o){
14743
14744     },
14745
14746     /**
14747          * @ignore
14748          */
14749     simpleAccess: function(obj, subsc) {
14750         return obj[subsc];
14751     },
14752
14753         /**
14754          * @ignore
14755          */
14756     getJsonAccessor: function(){
14757         var re = /[\[\.]/;
14758         return function(expr) {
14759             try {
14760                 return(re.test(expr))
14761                     ? new Function("obj", "return obj." + expr)
14762                     : function(obj){
14763                         return obj[expr];
14764                     };
14765             } catch(e){}
14766             return Roo.emptyFn;
14767         };
14768     }(),
14769
14770     /**
14771      * Create a data block containing Roo.data.Records from an XML document.
14772      * @param {Object} o An object which contains an Array of row objects in the property specified
14773      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14774      * which contains the total size of the dataset.
14775      * @return {Object} data A data block which is used by an Roo.data.Store object as
14776      * a cache of Roo.data.Records.
14777      */
14778     readRecords : function(o){
14779         /**
14780          * After any data loads, the raw JSON data is available for further custom processing.
14781          * @type Object
14782          */
14783         this.o = o;
14784         var s = this.meta, Record = this.recordType,
14785             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14786
14787 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
14788         if (!this.ef) {
14789             if(s.totalProperty) {
14790                     this.getTotal = this.getJsonAccessor(s.totalProperty);
14791                 }
14792                 if(s.successProperty) {
14793                     this.getSuccess = this.getJsonAccessor(s.successProperty);
14794                 }
14795                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14796                 if (s.id) {
14797                         var g = this.getJsonAccessor(s.id);
14798                         this.getId = function(rec) {
14799                                 var r = g(rec);  
14800                                 return (r === undefined || r === "") ? null : r;
14801                         };
14802                 } else {
14803                         this.getId = function(){return null;};
14804                 }
14805             this.ef = [];
14806             for(var jj = 0; jj < fl; jj++){
14807                 f = fi[jj];
14808                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14809                 this.ef[jj] = this.getJsonAccessor(map);
14810             }
14811         }
14812
14813         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14814         if(s.totalProperty){
14815             var vt = parseInt(this.getTotal(o), 10);
14816             if(!isNaN(vt)){
14817                 totalRecords = vt;
14818             }
14819         }
14820         if(s.successProperty){
14821             var vs = this.getSuccess(o);
14822             if(vs === false || vs === 'false'){
14823                 success = false;
14824             }
14825         }
14826         var records = [];
14827         for(var i = 0; i < c; i++){
14828                 var n = root[i];
14829             var values = {};
14830             var id = this.getId(n);
14831             for(var j = 0; j < fl; j++){
14832                 f = fi[j];
14833             var v = this.ef[j](n);
14834             if (!f.convert) {
14835                 Roo.log('missing convert for ' + f.name);
14836                 Roo.log(f);
14837                 continue;
14838             }
14839             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14840             }
14841             var record = new Record(values, id);
14842             record.json = n;
14843             records[i] = record;
14844         }
14845         return {
14846             raw : o,
14847             success : success,
14848             records : records,
14849             totalRecords : totalRecords
14850         };
14851     },
14852     // used when loading children.. @see loadDataFromChildren
14853     toLoadData: function(rec)
14854     {
14855         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14856         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14857         return { data : data, total : data.length };
14858         
14859     }
14860 });/*
14861  * Based on:
14862  * Ext JS Library 1.1.1
14863  * Copyright(c) 2006-2007, Ext JS, LLC.
14864  *
14865  * Originally Released Under LGPL - original licence link has changed is not relivant.
14866  *
14867  * Fork - LGPL
14868  * <script type="text/javascript">
14869  */
14870
14871 /**
14872  * @class Roo.data.ArrayReader
14873  * @extends Roo.data.DataReader
14874  * Data reader class to create an Array of Roo.data.Record objects from an Array.
14875  * Each element of that Array represents a row of data fields. The
14876  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14877  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14878  * <p>
14879  * Example code:.
14880  * <pre><code>
14881 var RecordDef = Roo.data.Record.create([
14882     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
14883     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
14884 ]);
14885 var myReader = new Roo.data.ArrayReader({
14886     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
14887 }, RecordDef);
14888 </code></pre>
14889  * <p>
14890  * This would consume an Array like this:
14891  * <pre><code>
14892 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14893   </code></pre>
14894  
14895  * @constructor
14896  * Create a new JsonReader
14897  * @param {Object} meta Metadata configuration options.
14898  * @param {Object|Array} recordType Either an Array of field definition objects
14899  * 
14900  * @cfg {Array} fields Array of field definition objects
14901  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14902  * as specified to {@link Roo.data.Record#create},
14903  * or an {@link Roo.data.Record} object
14904  *
14905  * 
14906  * created using {@link Roo.data.Record#create}.
14907  */
14908 Roo.data.ArrayReader = function(meta, recordType)
14909 {    
14910     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14911 };
14912
14913 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14914     
14915       /**
14916      * Create a data block containing Roo.data.Records from an XML document.
14917      * @param {Object} o An Array of row objects which represents the dataset.
14918      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14919      * a cache of Roo.data.Records.
14920      */
14921     readRecords : function(o)
14922     {
14923         var sid = this.meta ? this.meta.id : null;
14924         var recordType = this.recordType, fields = recordType.prototype.fields;
14925         var records = [];
14926         var root = o;
14927         for(var i = 0; i < root.length; i++){
14928                 var n = root[i];
14929             var values = {};
14930             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14931             for(var j = 0, jlen = fields.length; j < jlen; j++){
14932                 var f = fields.items[j];
14933                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14934                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14935                 v = f.convert(v);
14936                 values[f.name] = v;
14937             }
14938             var record = new recordType(values, id);
14939             record.json = n;
14940             records[records.length] = record;
14941         }
14942         return {
14943             records : records,
14944             totalRecords : records.length
14945         };
14946     },
14947     // used when loading children.. @see loadDataFromChildren
14948     toLoadData: function(rec)
14949     {
14950         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14951         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14952         
14953     }
14954     
14955     
14956 });/*
14957  * - LGPL
14958  * * 
14959  */
14960
14961 /**
14962  * @class Roo.bootstrap.ComboBox
14963  * @extends Roo.bootstrap.TriggerField
14964  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14965  * @cfg {Boolean} append (true|false) default false
14966  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14967  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14968  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14969  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14970  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14971  * @cfg {Boolean} animate default true
14972  * @cfg {Boolean} emptyResultText only for touch device
14973  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14974  * @cfg {String} emptyTitle default ''
14975  * @cfg {Number} width fixed with? experimental
14976  * @constructor
14977  * Create a new ComboBox.
14978  * @param {Object} config Configuration options
14979  */
14980 Roo.bootstrap.ComboBox = function(config){
14981     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14982     this.addEvents({
14983         /**
14984          * @event expand
14985          * Fires when the dropdown list is expanded
14986         * @param {Roo.bootstrap.ComboBox} combo This combo box
14987         */
14988         'expand' : true,
14989         /**
14990          * @event collapse
14991          * Fires when the dropdown list is collapsed
14992         * @param {Roo.bootstrap.ComboBox} combo This combo box
14993         */
14994         'collapse' : true,
14995         /**
14996          * @event beforeselect
14997          * Fires before a list item is selected. Return false to cancel the selection.
14998         * @param {Roo.bootstrap.ComboBox} combo This combo box
14999         * @param {Roo.data.Record} record The data record returned from the underlying store
15000         * @param {Number} index The index of the selected item in the dropdown list
15001         */
15002         'beforeselect' : true,
15003         /**
15004          * @event select
15005          * Fires when a list item is selected
15006         * @param {Roo.bootstrap.ComboBox} combo This combo box
15007         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15008         * @param {Number} index The index of the selected item in the dropdown list
15009         */
15010         'select' : true,
15011         /**
15012          * @event beforequery
15013          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15014          * The event object passed has these properties:
15015         * @param {Roo.bootstrap.ComboBox} combo This combo box
15016         * @param {String} query The query
15017         * @param {Boolean} forceAll true to force "all" query
15018         * @param {Boolean} cancel true to cancel the query
15019         * @param {Object} e The query event object
15020         */
15021         'beforequery': true,
15022          /**
15023          * @event add
15024          * Fires when the 'add' icon is pressed (add a listener to enable add button)
15025         * @param {Roo.bootstrap.ComboBox} combo This combo box
15026         */
15027         'add' : true,
15028         /**
15029          * @event edit
15030          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15031         * @param {Roo.bootstrap.ComboBox} combo This combo box
15032         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15033         */
15034         'edit' : true,
15035         /**
15036          * @event remove
15037          * Fires when the remove value from the combobox array
15038         * @param {Roo.bootstrap.ComboBox} combo This combo box
15039         */
15040         'remove' : true,
15041         /**
15042          * @event afterremove
15043          * Fires when the remove value from the combobox array
15044         * @param {Roo.bootstrap.ComboBox} combo This combo box
15045         */
15046         'afterremove' : true,
15047         /**
15048          * @event specialfilter
15049          * Fires when specialfilter
15050             * @param {Roo.bootstrap.ComboBox} combo This combo box
15051             */
15052         'specialfilter' : true,
15053         /**
15054          * @event tick
15055          * Fires when tick the element
15056             * @param {Roo.bootstrap.ComboBox} combo This combo box
15057             */
15058         'tick' : true,
15059         /**
15060          * @event touchviewdisplay
15061          * Fires when touch view require special display (default is using displayField)
15062             * @param {Roo.bootstrap.ComboBox} combo This combo box
15063             * @param {Object} cfg set html .
15064             */
15065         'touchviewdisplay' : true
15066         
15067     });
15068     
15069     this.item = [];
15070     this.tickItems = [];
15071     
15072     this.selectedIndex = -1;
15073     if(this.mode == 'local'){
15074         if(config.queryDelay === undefined){
15075             this.queryDelay = 10;
15076         }
15077         if(config.minChars === undefined){
15078             this.minChars = 0;
15079         }
15080     }
15081 };
15082
15083 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15084      
15085     /**
15086      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15087      * rendering into an Roo.Editor, defaults to false)
15088      */
15089     /**
15090      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15091      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15092      */
15093     /**
15094      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15095      */
15096     /**
15097      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15098      * the dropdown list (defaults to undefined, with no header element)
15099      */
15100
15101      /**
15102      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15103      */
15104      
15105      /**
15106      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15107      */
15108     listWidth: undefined,
15109     /**
15110      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15111      * mode = 'remote' or 'text' if mode = 'local')
15112      */
15113     displayField: undefined,
15114     
15115     /**
15116      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15117      * mode = 'remote' or 'value' if mode = 'local'). 
15118      * Note: use of a valueField requires the user make a selection
15119      * in order for a value to be mapped.
15120      */
15121     valueField: undefined,
15122     /**
15123      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15124      */
15125     modalTitle : '',
15126     
15127     /**
15128      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15129      * field's data value (defaults to the underlying DOM element's name)
15130      */
15131     hiddenName: undefined,
15132     /**
15133      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15134      */
15135     listClass: '',
15136     /**
15137      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15138      */
15139     selectedClass: 'active',
15140     
15141     /**
15142      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15143      */
15144     shadow:'sides',
15145     /**
15146      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15147      * anchor positions (defaults to 'tl-bl')
15148      */
15149     listAlign: 'tl-bl?',
15150     /**
15151      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15152      */
15153     maxHeight: 300,
15154     /**
15155      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15156      * query specified by the allQuery config option (defaults to 'query')
15157      */
15158     triggerAction: 'query',
15159     /**
15160      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15161      * (defaults to 4, does not apply if editable = false)
15162      */
15163     minChars : 4,
15164     /**
15165      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15166      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15167      */
15168     typeAhead: false,
15169     /**
15170      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15171      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15172      */
15173     queryDelay: 500,
15174     /**
15175      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15176      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15177      */
15178     pageSize: 0,
15179     /**
15180      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15181      * when editable = true (defaults to false)
15182      */
15183     selectOnFocus:false,
15184     /**
15185      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15186      */
15187     queryParam: 'query',
15188     /**
15189      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15190      * when mode = 'remote' (defaults to 'Loading...')
15191      */
15192     loadingText: 'Loading...',
15193     /**
15194      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15195      */
15196     resizable: false,
15197     /**
15198      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15199      */
15200     handleHeight : 8,
15201     /**
15202      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15203      * traditional select (defaults to true)
15204      */
15205     editable: true,
15206     /**
15207      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15208      */
15209     allQuery: '',
15210     /**
15211      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15212      */
15213     mode: 'remote',
15214     /**
15215      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15216      * listWidth has a higher value)
15217      */
15218     minListWidth : 70,
15219     /**
15220      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15221      * allow the user to set arbitrary text into the field (defaults to false)
15222      */
15223     forceSelection:false,
15224     /**
15225      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15226      * if typeAhead = true (defaults to 250)
15227      */
15228     typeAheadDelay : 250,
15229     /**
15230      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15231      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15232      */
15233     valueNotFoundText : undefined,
15234     /**
15235      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15236      */
15237     blockFocus : false,
15238     
15239     /**
15240      * @cfg {Boolean} disableClear Disable showing of clear button.
15241      */
15242     disableClear : false,
15243     /**
15244      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15245      */
15246     alwaysQuery : false,
15247     
15248     /**
15249      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15250      */
15251     multiple : false,
15252     
15253     /**
15254      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15255      */
15256     invalidClass : "has-warning",
15257     
15258     /**
15259      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15260      */
15261     validClass : "has-success",
15262     
15263     /**
15264      * @cfg {Boolean} specialFilter (true|false) special filter default false
15265      */
15266     specialFilter : false,
15267     
15268     /**
15269      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15270      */
15271     mobileTouchView : true,
15272     
15273     /**
15274      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15275      */
15276     useNativeIOS : false,
15277     
15278     /**
15279      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15280      */
15281     mobile_restrict_height : false,
15282     
15283     ios_options : false,
15284     
15285     //private
15286     addicon : false,
15287     editicon: false,
15288     
15289     page: 0,
15290     hasQuery: false,
15291     append: false,
15292     loadNext: false,
15293     autoFocus : true,
15294     tickable : false,
15295     btnPosition : 'right',
15296     triggerList : true,
15297     showToggleBtn : true,
15298     animate : true,
15299     emptyResultText: 'Empty',
15300     triggerText : 'Select',
15301     emptyTitle : '',
15302     width : false,
15303     
15304     // element that contains real text value.. (when hidden is used..)
15305     
15306     getAutoCreate : function()
15307     {   
15308         var cfg = false;
15309         //render
15310         /*
15311          * Render classic select for iso
15312          */
15313         
15314         if(Roo.isIOS && this.useNativeIOS){
15315             cfg = this.getAutoCreateNativeIOS();
15316             return cfg;
15317         }
15318         
15319         /*
15320          * Touch Devices
15321          */
15322         
15323         if(Roo.isTouch && this.mobileTouchView){
15324             cfg = this.getAutoCreateTouchView();
15325             return cfg;;
15326         }
15327         
15328         /*
15329          *  Normal ComboBox
15330          */
15331         if(!this.tickable){
15332             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15333             return cfg;
15334         }
15335         
15336         /*
15337          *  ComboBox with tickable selections
15338          */
15339              
15340         var align = this.labelAlign || this.parentLabelAlign();
15341         
15342         cfg = {
15343             cls : 'form-group roo-combobox-tickable' //input-group
15344         };
15345         
15346         var btn_text_select = '';
15347         var btn_text_done = '';
15348         var btn_text_cancel = '';
15349         
15350         if (this.btn_text_show) {
15351             btn_text_select = 'Select';
15352             btn_text_done = 'Done';
15353             btn_text_cancel = 'Cancel'; 
15354         }
15355         
15356         var buttons = {
15357             tag : 'div',
15358             cls : 'tickable-buttons',
15359             cn : [
15360                 {
15361                     tag : 'button',
15362                     type : 'button',
15363                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15364                     //html : this.triggerText
15365                     html: btn_text_select
15366                 },
15367                 {
15368                     tag : 'button',
15369                     type : 'button',
15370                     name : 'ok',
15371                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15372                     //html : 'Done'
15373                     html: btn_text_done
15374                 },
15375                 {
15376                     tag : 'button',
15377                     type : 'button',
15378                     name : 'cancel',
15379                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15380                     //html : 'Cancel'
15381                     html: btn_text_cancel
15382                 }
15383             ]
15384         };
15385         
15386         if(this.editable){
15387             buttons.cn.unshift({
15388                 tag: 'input',
15389                 cls: 'roo-select2-search-field-input'
15390             });
15391         }
15392         
15393         var _this = this;
15394         
15395         Roo.each(buttons.cn, function(c){
15396             if (_this.size) {
15397                 c.cls += ' btn-' + _this.size;
15398             }
15399
15400             if (_this.disabled) {
15401                 c.disabled = true;
15402             }
15403         });
15404         
15405         var box = {
15406             tag: 'div',
15407             style : 'display: contents',
15408             cn: [
15409                 {
15410                     tag: 'input',
15411                     type : 'hidden',
15412                     cls: 'form-hidden-field'
15413                 },
15414                 {
15415                     tag: 'ul',
15416                     cls: 'roo-select2-choices',
15417                     cn:[
15418                         {
15419                             tag: 'li',
15420                             cls: 'roo-select2-search-field',
15421                             cn: [
15422                                 buttons
15423                             ]
15424                         }
15425                     ]
15426                 }
15427             ]
15428         };
15429         
15430         var combobox = {
15431             cls: 'roo-select2-container input-group roo-select2-container-multi',
15432             cn: [
15433                 
15434                 box
15435 //                {
15436 //                    tag: 'ul',
15437 //                    cls: 'typeahead typeahead-long dropdown-menu',
15438 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15439 //                }
15440             ]
15441         };
15442         
15443         if(this.hasFeedback && !this.allowBlank){
15444             
15445             var feedback = {
15446                 tag: 'span',
15447                 cls: 'glyphicon form-control-feedback'
15448             };
15449
15450             combobox.cn.push(feedback);
15451         }
15452         
15453         
15454         
15455         var indicator = {
15456             tag : 'i',
15457             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15458             tooltip : 'This field is required'
15459         };
15460         if (Roo.bootstrap.version == 4) {
15461             indicator = {
15462                 tag : 'i',
15463                 style : 'display:none'
15464             };
15465         }
15466         if (align ==='left' && this.fieldLabel.length) {
15467             
15468             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15469             
15470             cfg.cn = [
15471                 indicator,
15472                 {
15473                     tag: 'label',
15474                     'for' :  id,
15475                     cls : 'control-label col-form-label',
15476                     html : this.fieldLabel
15477
15478                 },
15479                 {
15480                     cls : "", 
15481                     cn: [
15482                         combobox
15483                     ]
15484                 }
15485
15486             ];
15487             
15488             var labelCfg = cfg.cn[1];
15489             var contentCfg = cfg.cn[2];
15490             
15491
15492             if(this.indicatorpos == 'right'){
15493                 
15494                 cfg.cn = [
15495                     {
15496                         tag: 'label',
15497                         'for' :  id,
15498                         cls : 'control-label col-form-label',
15499                         cn : [
15500                             {
15501                                 tag : 'span',
15502                                 html : this.fieldLabel
15503                             },
15504                             indicator
15505                         ]
15506                     },
15507                     {
15508                         cls : "",
15509                         cn: [
15510                             combobox
15511                         ]
15512                     }
15513
15514                 ];
15515                 
15516                 
15517                 
15518                 labelCfg = cfg.cn[0];
15519                 contentCfg = cfg.cn[1];
15520             
15521             }
15522             
15523             if(this.labelWidth > 12){
15524                 labelCfg.style = "width: " + this.labelWidth + 'px';
15525             }
15526             if(this.width * 1 > 0){
15527                 contentCfg.style = "width: " + this.width + 'px';
15528             }
15529             if(this.labelWidth < 13 && this.labelmd == 0){
15530                 this.labelmd = this.labelWidth;
15531             }
15532             
15533             if(this.labellg > 0){
15534                 labelCfg.cls += ' col-lg-' + this.labellg;
15535                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15536             }
15537             
15538             if(this.labelmd > 0){
15539                 labelCfg.cls += ' col-md-' + this.labelmd;
15540                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15541             }
15542             
15543             if(this.labelsm > 0){
15544                 labelCfg.cls += ' col-sm-' + this.labelsm;
15545                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15546             }
15547             
15548             if(this.labelxs > 0){
15549                 labelCfg.cls += ' col-xs-' + this.labelxs;
15550                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15551             }
15552                 
15553                 
15554         } else if ( this.fieldLabel.length) {
15555 //                Roo.log(" label");
15556                  cfg.cn = [
15557                    indicator,
15558                     {
15559                         tag: 'label',
15560                         //cls : 'input-group-addon',
15561                         html : this.fieldLabel
15562                     },
15563                     combobox
15564                 ];
15565                 
15566                 if(this.indicatorpos == 'right'){
15567                     cfg.cn = [
15568                         {
15569                             tag: 'label',
15570                             //cls : 'input-group-addon',
15571                             html : this.fieldLabel
15572                         },
15573                         indicator,
15574                         combobox
15575                     ];
15576                     
15577                 }
15578
15579         } else {
15580             
15581 //                Roo.log(" no label && no align");
15582                 cfg = combobox
15583                      
15584                 
15585         }
15586          
15587         var settings=this;
15588         ['xs','sm','md','lg'].map(function(size){
15589             if (settings[size]) {
15590                 cfg.cls += ' col-' + size + '-' + settings[size];
15591             }
15592         });
15593         
15594         return cfg;
15595         
15596     },
15597     
15598     _initEventsCalled : false,
15599     
15600     // private
15601     initEvents: function()
15602     {   
15603         if (this._initEventsCalled) { // as we call render... prevent looping...
15604             return;
15605         }
15606         this._initEventsCalled = true;
15607         
15608         if (!this.store) {
15609             throw "can not find store for combo";
15610         }
15611         
15612         this.indicator = this.indicatorEl();
15613         
15614         this.store = Roo.factory(this.store, Roo.data);
15615         this.store.parent = this;
15616         
15617         // if we are building from html. then this element is so complex, that we can not really
15618         // use the rendered HTML.
15619         // so we have to trash and replace the previous code.
15620         if (Roo.XComponent.build_from_html) {
15621             // remove this element....
15622             var e = this.el.dom, k=0;
15623             while (e ) { e = e.previousSibling;  ++k;}
15624
15625             this.el.remove();
15626             
15627             this.el=false;
15628             this.rendered = false;
15629             
15630             this.render(this.parent().getChildContainer(true), k);
15631         }
15632         
15633         if(Roo.isIOS && this.useNativeIOS){
15634             this.initIOSView();
15635             return;
15636         }
15637         
15638         /*
15639          * Touch Devices
15640          */
15641         
15642         if(Roo.isTouch && this.mobileTouchView){
15643             this.initTouchView();
15644             return;
15645         }
15646         
15647         if(this.tickable){
15648             this.initTickableEvents();
15649             return;
15650         }
15651         
15652         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15653         
15654         if(this.hiddenName){
15655             
15656             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15657             
15658             this.hiddenField.dom.value =
15659                 this.hiddenValue !== undefined ? this.hiddenValue :
15660                 this.value !== undefined ? this.value : '';
15661
15662             // prevent input submission
15663             this.el.dom.removeAttribute('name');
15664             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15665              
15666              
15667         }
15668         //if(Roo.isGecko){
15669         //    this.el.dom.setAttribute('autocomplete', 'off');
15670         //}
15671         
15672         var cls = 'x-combo-list';
15673         
15674         //this.list = new Roo.Layer({
15675         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15676         //});
15677         
15678         var _this = this;
15679         
15680         (function(){
15681             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15682             _this.list.setWidth(lw);
15683         }).defer(100);
15684         
15685         this.list.on('mouseover', this.onViewOver, this);
15686         this.list.on('mousemove', this.onViewMove, this);
15687         this.list.on('scroll', this.onViewScroll, this);
15688         
15689         /*
15690         this.list.swallowEvent('mousewheel');
15691         this.assetHeight = 0;
15692
15693         if(this.title){
15694             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15695             this.assetHeight += this.header.getHeight();
15696         }
15697
15698         this.innerList = this.list.createChild({cls:cls+'-inner'});
15699         this.innerList.on('mouseover', this.onViewOver, this);
15700         this.innerList.on('mousemove', this.onViewMove, this);
15701         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15702         
15703         if(this.allowBlank && !this.pageSize && !this.disableClear){
15704             this.footer = this.list.createChild({cls:cls+'-ft'});
15705             this.pageTb = new Roo.Toolbar(this.footer);
15706            
15707         }
15708         if(this.pageSize){
15709             this.footer = this.list.createChild({cls:cls+'-ft'});
15710             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15711                     {pageSize: this.pageSize});
15712             
15713         }
15714         
15715         if (this.pageTb && this.allowBlank && !this.disableClear) {
15716             var _this = this;
15717             this.pageTb.add(new Roo.Toolbar.Fill(), {
15718                 cls: 'x-btn-icon x-btn-clear',
15719                 text: '&#160;',
15720                 handler: function()
15721                 {
15722                     _this.collapse();
15723                     _this.clearValue();
15724                     _this.onSelect(false, -1);
15725                 }
15726             });
15727         }
15728         if (this.footer) {
15729             this.assetHeight += this.footer.getHeight();
15730         }
15731         */
15732             
15733         if(!this.tpl){
15734             this.tpl = Roo.bootstrap.version == 4 ?
15735                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15736                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15737         }
15738
15739         this.view = new Roo.View(this.list, this.tpl, {
15740             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15741         });
15742         //this.view.wrapEl.setDisplayed(false);
15743         this.view.on('click', this.onViewClick, this);
15744         
15745         
15746         this.store.on('beforeload', this.onBeforeLoad, this);
15747         this.store.on('load', this.onLoad, this);
15748         this.store.on('loadexception', this.onLoadException, this);
15749         /*
15750         if(this.resizable){
15751             this.resizer = new Roo.Resizable(this.list,  {
15752                pinned:true, handles:'se'
15753             });
15754             this.resizer.on('resize', function(r, w, h){
15755                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15756                 this.listWidth = w;
15757                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15758                 this.restrictHeight();
15759             }, this);
15760             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15761         }
15762         */
15763         if(!this.editable){
15764             this.editable = true;
15765             this.setEditable(false);
15766         }
15767         
15768         /*
15769         
15770         if (typeof(this.events.add.listeners) != 'undefined') {
15771             
15772             this.addicon = this.wrap.createChild(
15773                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
15774        
15775             this.addicon.on('click', function(e) {
15776                 this.fireEvent('add', this);
15777             }, this);
15778         }
15779         if (typeof(this.events.edit.listeners) != 'undefined') {
15780             
15781             this.editicon = this.wrap.createChild(
15782                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
15783             if (this.addicon) {
15784                 this.editicon.setStyle('margin-left', '40px');
15785             }
15786             this.editicon.on('click', function(e) {
15787                 
15788                 // we fire even  if inothing is selected..
15789                 this.fireEvent('edit', this, this.lastData );
15790                 
15791             }, this);
15792         }
15793         */
15794         
15795         this.keyNav = new Roo.KeyNav(this.inputEl(), {
15796             "up" : function(e){
15797                 this.inKeyMode = true;
15798                 this.selectPrev();
15799             },
15800
15801             "down" : function(e){
15802                 if(!this.isExpanded()){
15803                     this.onTriggerClick();
15804                 }else{
15805                     this.inKeyMode = true;
15806                     this.selectNext();
15807                 }
15808             },
15809
15810             "enter" : function(e){
15811 //                this.onViewClick();
15812                 //return true;
15813                 this.collapse();
15814                 
15815                 if(this.fireEvent("specialkey", this, e)){
15816                     this.onViewClick(false);
15817                 }
15818                 
15819                 return true;
15820             },
15821
15822             "esc" : function(e){
15823                 this.collapse();
15824             },
15825
15826             "tab" : function(e){
15827                 this.collapse();
15828                 
15829                 if(this.fireEvent("specialkey", this, e)){
15830                     this.onViewClick(false);
15831                 }
15832                 
15833                 return true;
15834             },
15835
15836             scope : this,
15837
15838             doRelay : function(foo, bar, hname){
15839                 if(hname == 'down' || this.scope.isExpanded()){
15840                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15841                 }
15842                 return true;
15843             },
15844
15845             forceKeyDown: true
15846         });
15847         
15848         
15849         this.queryDelay = Math.max(this.queryDelay || 10,
15850                 this.mode == 'local' ? 10 : 250);
15851         
15852         
15853         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15854         
15855         if(this.typeAhead){
15856             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15857         }
15858         if(this.editable !== false){
15859             this.inputEl().on("keyup", this.onKeyUp, this);
15860         }
15861         if(this.forceSelection){
15862             this.inputEl().on('blur', this.doForce, this);
15863         }
15864         
15865         if(this.multiple){
15866             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15867             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15868         }
15869     },
15870     
15871     initTickableEvents: function()
15872     {   
15873         this.createList();
15874         
15875         if(this.hiddenName){
15876             
15877             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15878             
15879             this.hiddenField.dom.value =
15880                 this.hiddenValue !== undefined ? this.hiddenValue :
15881                 this.value !== undefined ? this.value : '';
15882
15883             // prevent input submission
15884             this.el.dom.removeAttribute('name');
15885             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15886              
15887              
15888         }
15889         
15890 //        this.list = this.el.select('ul.dropdown-menu',true).first();
15891         
15892         this.choices = this.el.select('ul.roo-select2-choices', true).first();
15893         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15894         if(this.triggerList){
15895             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15896         }
15897          
15898         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15899         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15900         
15901         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15902         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15903         
15904         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15905         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15906         
15907         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15908         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15909         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15910         
15911         this.okBtn.hide();
15912         this.cancelBtn.hide();
15913         
15914         var _this = this;
15915         
15916         (function(){
15917             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15918             _this.list.setWidth(lw);
15919         }).defer(100);
15920         
15921         this.list.on('mouseover', this.onViewOver, this);
15922         this.list.on('mousemove', this.onViewMove, this);
15923         
15924         this.list.on('scroll', this.onViewScroll, this);
15925         
15926         if(!this.tpl){
15927             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
15928                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15929         }
15930
15931         this.view = new Roo.View(this.list, this.tpl, {
15932             singleSelect:true,
15933             tickable:true,
15934             parent:this,
15935             store: this.store,
15936             selectedClass: this.selectedClass
15937         });
15938         
15939         //this.view.wrapEl.setDisplayed(false);
15940         this.view.on('click', this.onViewClick, this);
15941         
15942         
15943         
15944         this.store.on('beforeload', this.onBeforeLoad, this);
15945         this.store.on('load', this.onLoad, this);
15946         this.store.on('loadexception', this.onLoadException, this);
15947         
15948         if(this.editable){
15949             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15950                 "up" : function(e){
15951                     this.inKeyMode = true;
15952                     this.selectPrev();
15953                 },
15954
15955                 "down" : function(e){
15956                     this.inKeyMode = true;
15957                     this.selectNext();
15958                 },
15959
15960                 "enter" : function(e){
15961                     if(this.fireEvent("specialkey", this, e)){
15962                         this.onViewClick(false);
15963                     }
15964                     
15965                     return true;
15966                 },
15967
15968                 "esc" : function(e){
15969                     this.onTickableFooterButtonClick(e, false, false);
15970                 },
15971
15972                 "tab" : function(e){
15973                     this.fireEvent("specialkey", this, e);
15974                     
15975                     this.onTickableFooterButtonClick(e, false, false);
15976                     
15977                     return true;
15978                 },
15979
15980                 scope : this,
15981
15982                 doRelay : function(e, fn, key){
15983                     if(this.scope.isExpanded()){
15984                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15985                     }
15986                     return true;
15987                 },
15988
15989                 forceKeyDown: true
15990             });
15991         }
15992         
15993         this.queryDelay = Math.max(this.queryDelay || 10,
15994                 this.mode == 'local' ? 10 : 250);
15995         
15996         
15997         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15998         
15999         if(this.typeAhead){
16000             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16001         }
16002         
16003         if(this.editable !== false){
16004             this.tickableInputEl().on("keyup", this.onKeyUp, this);
16005         }
16006         
16007         this.indicator = this.indicatorEl();
16008         
16009         if(this.indicator){
16010             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16011             this.indicator.hide();
16012         }
16013         
16014     },
16015
16016     onDestroy : function(){
16017         if(this.view){
16018             this.view.setStore(null);
16019             this.view.el.removeAllListeners();
16020             this.view.el.remove();
16021             this.view.purgeListeners();
16022         }
16023         if(this.list){
16024             this.list.dom.innerHTML  = '';
16025         }
16026         
16027         if(this.store){
16028             this.store.un('beforeload', this.onBeforeLoad, this);
16029             this.store.un('load', this.onLoad, this);
16030             this.store.un('loadexception', this.onLoadException, this);
16031         }
16032         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16033     },
16034
16035     // private
16036     fireKey : function(e){
16037         if(e.isNavKeyPress() && !this.list.isVisible()){
16038             this.fireEvent("specialkey", this, e);
16039         }
16040     },
16041
16042     // private
16043     onResize: function(w, h)
16044     {
16045         
16046         
16047 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16048 //        
16049 //        if(typeof w != 'number'){
16050 //            // we do not handle it!?!?
16051 //            return;
16052 //        }
16053 //        var tw = this.trigger.getWidth();
16054 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
16055 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
16056 //        var x = w - tw;
16057 //        this.inputEl().setWidth( this.adjustWidth('input', x));
16058 //            
16059 //        //this.trigger.setStyle('left', x+'px');
16060 //        
16061 //        if(this.list && this.listWidth === undefined){
16062 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16063 //            this.list.setWidth(lw);
16064 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16065 //        }
16066         
16067     
16068         
16069     },
16070
16071     /**
16072      * Allow or prevent the user from directly editing the field text.  If false is passed,
16073      * the user will only be able to select from the items defined in the dropdown list.  This method
16074      * is the runtime equivalent of setting the 'editable' config option at config time.
16075      * @param {Boolean} value True to allow the user to directly edit the field text
16076      */
16077     setEditable : function(value){
16078         if(value == this.editable){
16079             return;
16080         }
16081         this.editable = value;
16082         if(!value){
16083             this.inputEl().dom.setAttribute('readOnly', true);
16084             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16085             this.inputEl().addClass('x-combo-noedit');
16086         }else{
16087             this.inputEl().dom.setAttribute('readOnly', false);
16088             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16089             this.inputEl().removeClass('x-combo-noedit');
16090         }
16091     },
16092
16093     // private
16094     
16095     onBeforeLoad : function(combo,opts){
16096         if(!this.hasFocus){
16097             return;
16098         }
16099          if (!opts.add) {
16100             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16101          }
16102         this.restrictHeight();
16103         this.selectedIndex = -1;
16104     },
16105
16106     // private
16107     onLoad : function(){
16108         
16109         this.hasQuery = false;
16110         
16111         if(!this.hasFocus){
16112             return;
16113         }
16114         
16115         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16116             this.loading.hide();
16117         }
16118         
16119         if(this.store.getCount() > 0){
16120             
16121             this.expand();
16122             this.restrictHeight();
16123             if(this.lastQuery == this.allQuery){
16124                 if(this.editable && !this.tickable){
16125                     this.inputEl().dom.select();
16126                 }
16127                 
16128                 if(
16129                     !this.selectByValue(this.value, true) &&
16130                     this.autoFocus && 
16131                     (
16132                         !this.store.lastOptions ||
16133                         typeof(this.store.lastOptions.add) == 'undefined' || 
16134                         this.store.lastOptions.add != true
16135                     )
16136                 ){
16137                     this.select(0, true);
16138                 }
16139             }else{
16140                 if(this.autoFocus){
16141                     this.selectNext();
16142                 }
16143                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16144                     this.taTask.delay(this.typeAheadDelay);
16145                 }
16146             }
16147         }else{
16148             this.onEmptyResults();
16149         }
16150         
16151         //this.el.focus();
16152     },
16153     // private
16154     onLoadException : function()
16155     {
16156         this.hasQuery = false;
16157         
16158         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16159             this.loading.hide();
16160         }
16161         
16162         if(this.tickable && this.editable){
16163             return;
16164         }
16165         
16166         this.collapse();
16167         // only causes errors at present
16168         //Roo.log(this.store.reader.jsonData);
16169         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16170             // fixme
16171             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16172         //}
16173         
16174         
16175     },
16176     // private
16177     onTypeAhead : function(){
16178         if(this.store.getCount() > 0){
16179             var r = this.store.getAt(0);
16180             var newValue = r.data[this.displayField];
16181             var len = newValue.length;
16182             var selStart = this.getRawValue().length;
16183             
16184             if(selStart != len){
16185                 this.setRawValue(newValue);
16186                 this.selectText(selStart, newValue.length);
16187             }
16188         }
16189     },
16190
16191     // private
16192     onSelect : function(record, index){
16193         
16194         if(this.fireEvent('beforeselect', this, record, index) !== false){
16195         
16196             this.setFromData(index > -1 ? record.data : false);
16197             
16198             this.collapse();
16199             this.fireEvent('select', this, record, index);
16200         }
16201     },
16202
16203     /**
16204      * Returns the currently selected field value or empty string if no value is set.
16205      * @return {String} value The selected value
16206      */
16207     getValue : function()
16208     {
16209         if(Roo.isIOS && this.useNativeIOS){
16210             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16211         }
16212         
16213         if(this.multiple){
16214             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16215         }
16216         
16217         if(this.valueField){
16218             return typeof this.value != 'undefined' ? this.value : '';
16219         }else{
16220             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16221         }
16222     },
16223     
16224     getRawValue : function()
16225     {
16226         if(Roo.isIOS && this.useNativeIOS){
16227             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16228         }
16229         
16230         var v = this.inputEl().getValue();
16231         
16232         return v;
16233     },
16234
16235     /**
16236      * Clears any text/value currently set in the field
16237      */
16238     clearValue : function(){
16239         
16240         if(this.hiddenField){
16241             this.hiddenField.dom.value = '';
16242         }
16243         this.value = '';
16244         this.setRawValue('');
16245         this.lastSelectionText = '';
16246         this.lastData = false;
16247         
16248         var close = this.closeTriggerEl();
16249         
16250         if(close){
16251             close.hide();
16252         }
16253         
16254         this.validate();
16255         
16256     },
16257
16258     /**
16259      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16260      * will be displayed in the field.  If the value does not match the data value of an existing item,
16261      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16262      * Otherwise the field will be blank (although the value will still be set).
16263      * @param {String} value The value to match
16264      */
16265     setValue : function(v)
16266     {
16267         if(Roo.isIOS && this.useNativeIOS){
16268             this.setIOSValue(v);
16269             return;
16270         }
16271         
16272         if(this.multiple){
16273             this.syncValue();
16274             return;
16275         }
16276         
16277         var text = v;
16278         if(this.valueField){
16279             var r = this.findRecord(this.valueField, v);
16280             if(r){
16281                 text = r.data[this.displayField];
16282             }else if(this.valueNotFoundText !== undefined){
16283                 text = this.valueNotFoundText;
16284             }
16285         }
16286         this.lastSelectionText = text;
16287         if(this.hiddenField){
16288             this.hiddenField.dom.value = v;
16289         }
16290         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16291         this.value = v;
16292         
16293         var close = this.closeTriggerEl();
16294         
16295         if(close){
16296             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16297         }
16298         
16299         this.validate();
16300     },
16301     /**
16302      * @property {Object} the last set data for the element
16303      */
16304     
16305     lastData : false,
16306     /**
16307      * Sets the value of the field based on a object which is related to the record format for the store.
16308      * @param {Object} value the value to set as. or false on reset?
16309      */
16310     setFromData : function(o){
16311         
16312         if(this.multiple){
16313             this.addItem(o);
16314             return;
16315         }
16316             
16317         var dv = ''; // display value
16318         var vv = ''; // value value..
16319         this.lastData = o;
16320         if (this.displayField) {
16321             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16322         } else {
16323             // this is an error condition!!!
16324             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16325         }
16326         
16327         if(this.valueField){
16328             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16329         }
16330         
16331         var close = this.closeTriggerEl();
16332         
16333         if(close){
16334             if(dv.length || vv * 1 > 0){
16335                 close.show() ;
16336                 this.blockFocus=true;
16337             } else {
16338                 close.hide();
16339             }             
16340         }
16341         
16342         if(this.hiddenField){
16343             this.hiddenField.dom.value = vv;
16344             
16345             this.lastSelectionText = dv;
16346             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16347             this.value = vv;
16348             return;
16349         }
16350         // no hidden field.. - we store the value in 'value', but still display
16351         // display field!!!!
16352         this.lastSelectionText = dv;
16353         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16354         this.value = vv;
16355         
16356         
16357         
16358     },
16359     // private
16360     reset : function(){
16361         // overridden so that last data is reset..
16362         
16363         if(this.multiple){
16364             this.clearItem();
16365             return;
16366         }
16367         
16368         this.setValue(this.originalValue);
16369         //this.clearInvalid();
16370         this.lastData = false;
16371         if (this.view) {
16372             this.view.clearSelections();
16373         }
16374         
16375         this.validate();
16376     },
16377     // private
16378     findRecord : function(prop, value){
16379         var record;
16380         if(this.store.getCount() > 0){
16381             this.store.each(function(r){
16382                 if(r.data[prop] == value){
16383                     record = r;
16384                     return false;
16385                 }
16386                 return true;
16387             });
16388         }
16389         return record;
16390     },
16391     
16392     getName: function()
16393     {
16394         // returns hidden if it's set..
16395         if (!this.rendered) {return ''};
16396         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16397         
16398     },
16399     // private
16400     onViewMove : function(e, t){
16401         this.inKeyMode = false;
16402     },
16403
16404     // private
16405     onViewOver : function(e, t){
16406         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16407             return;
16408         }
16409         var item = this.view.findItemFromChild(t);
16410         
16411         if(item){
16412             var index = this.view.indexOf(item);
16413             this.select(index, false);
16414         }
16415     },
16416
16417     // private
16418     onViewClick : function(view, doFocus, el, e)
16419     {
16420         var index = this.view.getSelectedIndexes()[0];
16421         
16422         var r = this.store.getAt(index);
16423         
16424         if(this.tickable){
16425             
16426             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16427                 return;
16428             }
16429             
16430             var rm = false;
16431             var _this = this;
16432             
16433             Roo.each(this.tickItems, function(v,k){
16434                 
16435                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16436                     Roo.log(v);
16437                     _this.tickItems.splice(k, 1);
16438                     
16439                     if(typeof(e) == 'undefined' && view == false){
16440                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16441                     }
16442                     
16443                     rm = true;
16444                     return;
16445                 }
16446             });
16447             
16448             if(rm){
16449                 return;
16450             }
16451             
16452             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16453                 this.tickItems.push(r.data);
16454             }
16455             
16456             if(typeof(e) == 'undefined' && view == false){
16457                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16458             }
16459                     
16460             return;
16461         }
16462         
16463         if(r){
16464             this.onSelect(r, index);
16465         }
16466         if(doFocus !== false && !this.blockFocus){
16467             this.inputEl().focus();
16468         }
16469     },
16470
16471     // private
16472     restrictHeight : function(){
16473         //this.innerList.dom.style.height = '';
16474         //var inner = this.innerList.dom;
16475         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16476         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16477         //this.list.beginUpdate();
16478         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16479         this.list.alignTo(this.inputEl(), this.listAlign);
16480         this.list.alignTo(this.inputEl(), this.listAlign);
16481         //this.list.endUpdate();
16482     },
16483
16484     // private
16485     onEmptyResults : function(){
16486         
16487         if(this.tickable && this.editable){
16488             this.hasFocus = false;
16489             this.restrictHeight();
16490             return;
16491         }
16492         
16493         this.collapse();
16494     },
16495
16496     /**
16497      * Returns true if the dropdown list is expanded, else false.
16498      */
16499     isExpanded : function(){
16500         return this.list.isVisible();
16501     },
16502
16503     /**
16504      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16505      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16506      * @param {String} value The data value of the item to select
16507      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16508      * selected item if it is not currently in view (defaults to true)
16509      * @return {Boolean} True if the value matched an item in the list, else false
16510      */
16511     selectByValue : function(v, scrollIntoView){
16512         if(v !== undefined && v !== null){
16513             var r = this.findRecord(this.valueField || this.displayField, v);
16514             if(r){
16515                 this.select(this.store.indexOf(r), scrollIntoView);
16516                 return true;
16517             }
16518         }
16519         return false;
16520     },
16521
16522     /**
16523      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16524      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16525      * @param {Number} index The zero-based index of the list item to select
16526      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16527      * selected item if it is not currently in view (defaults to true)
16528      */
16529     select : function(index, scrollIntoView){
16530         this.selectedIndex = index;
16531         this.view.select(index);
16532         if(scrollIntoView !== false){
16533             var el = this.view.getNode(index);
16534             /*
16535              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16536              */
16537             if(el){
16538                 this.list.scrollChildIntoView(el, false);
16539             }
16540         }
16541     },
16542
16543     // private
16544     selectNext : function(){
16545         var ct = this.store.getCount();
16546         if(ct > 0){
16547             if(this.selectedIndex == -1){
16548                 this.select(0);
16549             }else if(this.selectedIndex < ct-1){
16550                 this.select(this.selectedIndex+1);
16551             }
16552         }
16553     },
16554
16555     // private
16556     selectPrev : function(){
16557         var ct = this.store.getCount();
16558         if(ct > 0){
16559             if(this.selectedIndex == -1){
16560                 this.select(0);
16561             }else if(this.selectedIndex != 0){
16562                 this.select(this.selectedIndex-1);
16563             }
16564         }
16565     },
16566
16567     // private
16568     onKeyUp : function(e){
16569         if(this.editable !== false && !e.isSpecialKey()){
16570             this.lastKey = e.getKey();
16571             this.dqTask.delay(this.queryDelay);
16572         }
16573     },
16574
16575     // private
16576     validateBlur : function(){
16577         return !this.list || !this.list.isVisible();   
16578     },
16579
16580     // private
16581     initQuery : function(){
16582         
16583         var v = this.getRawValue();
16584         
16585         if(this.tickable && this.editable){
16586             v = this.tickableInputEl().getValue();
16587         }
16588         
16589         this.doQuery(v);
16590     },
16591
16592     // private
16593     doForce : function(){
16594         if(this.inputEl().dom.value.length > 0){
16595             this.inputEl().dom.value =
16596                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16597              
16598         }
16599     },
16600
16601     /**
16602      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16603      * query allowing the query action to be canceled if needed.
16604      * @param {String} query The SQL query to execute
16605      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16606      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16607      * saved in the current store (defaults to false)
16608      */
16609     doQuery : function(q, forceAll){
16610         
16611         if(q === undefined || q === null){
16612             q = '';
16613         }
16614         var qe = {
16615             query: q,
16616             forceAll: forceAll,
16617             combo: this,
16618             cancel:false
16619         };
16620         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16621             return false;
16622         }
16623         q = qe.query;
16624         
16625         forceAll = qe.forceAll;
16626         if(forceAll === true || (q.length >= this.minChars)){
16627             
16628             this.hasQuery = true;
16629             
16630             if(this.lastQuery != q || this.alwaysQuery){
16631                 this.lastQuery = q;
16632                 if(this.mode == 'local'){
16633                     this.selectedIndex = -1;
16634                     if(forceAll){
16635                         this.store.clearFilter();
16636                     }else{
16637                         
16638                         if(this.specialFilter){
16639                             this.fireEvent('specialfilter', this);
16640                             this.onLoad();
16641                             return;
16642                         }
16643                         
16644                         this.store.filter(this.displayField, q);
16645                     }
16646                     
16647                     this.store.fireEvent("datachanged", this.store);
16648                     
16649                     this.onLoad();
16650                     
16651                     
16652                 }else{
16653                     
16654                     this.store.baseParams[this.queryParam] = q;
16655                     
16656                     var options = {params : this.getParams(q)};
16657                     
16658                     if(this.loadNext){
16659                         options.add = true;
16660                         options.params.start = this.page * this.pageSize;
16661                     }
16662                     
16663                     this.store.load(options);
16664                     
16665                     /*
16666                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16667                      *  we should expand the list on onLoad
16668                      *  so command out it
16669                      */
16670 //                    this.expand();
16671                 }
16672             }else{
16673                 this.selectedIndex = -1;
16674                 this.onLoad();   
16675             }
16676         }
16677         
16678         this.loadNext = false;
16679     },
16680     
16681     // private
16682     getParams : function(q){
16683         var p = {};
16684         //p[this.queryParam] = q;
16685         
16686         if(this.pageSize){
16687             p.start = 0;
16688             p.limit = this.pageSize;
16689         }
16690         return p;
16691     },
16692
16693     /**
16694      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16695      */
16696     collapse : function(){
16697         if(!this.isExpanded()){
16698             return;
16699         }
16700         
16701         this.list.hide();
16702         
16703         this.hasFocus = false;
16704         
16705         if(this.tickable){
16706             this.okBtn.hide();
16707             this.cancelBtn.hide();
16708             this.trigger.show();
16709             
16710             if(this.editable){
16711                 this.tickableInputEl().dom.value = '';
16712                 this.tickableInputEl().blur();
16713             }
16714             
16715         }
16716         
16717         Roo.get(document).un('mousedown', this.collapseIf, this);
16718         Roo.get(document).un('mousewheel', this.collapseIf, this);
16719         if (!this.editable) {
16720             Roo.get(document).un('keydown', this.listKeyPress, this);
16721         }
16722         this.fireEvent('collapse', this);
16723         
16724         this.validate();
16725     },
16726
16727     // private
16728     collapseIf : function(e){
16729         var in_combo  = e.within(this.el);
16730         var in_list =  e.within(this.list);
16731         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16732         
16733         if (in_combo || in_list || is_list) {
16734             //e.stopPropagation();
16735             return;
16736         }
16737         
16738         if(this.tickable){
16739             this.onTickableFooterButtonClick(e, false, false);
16740         }
16741
16742         this.collapse();
16743         
16744     },
16745
16746     /**
16747      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16748      */
16749     expand : function(){
16750        
16751         if(this.isExpanded() || !this.hasFocus){
16752             return;
16753         }
16754         
16755         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16756         this.list.setWidth(lw);
16757         
16758         Roo.log('expand');
16759         
16760         this.list.show();
16761         
16762         this.restrictHeight();
16763         
16764         if(this.tickable){
16765             
16766             this.tickItems = Roo.apply([], this.item);
16767             
16768             this.okBtn.show();
16769             this.cancelBtn.show();
16770             this.trigger.hide();
16771             
16772             if(this.editable){
16773                 this.tickableInputEl().focus();
16774             }
16775             
16776         }
16777         
16778         Roo.get(document).on('mousedown', this.collapseIf, this);
16779         Roo.get(document).on('mousewheel', this.collapseIf, this);
16780         if (!this.editable) {
16781             Roo.get(document).on('keydown', this.listKeyPress, this);
16782         }
16783         
16784         this.fireEvent('expand', this);
16785     },
16786
16787     // private
16788     // Implements the default empty TriggerField.onTriggerClick function
16789     onTriggerClick : function(e)
16790     {
16791         Roo.log('trigger click');
16792         
16793         if(this.disabled || !this.triggerList){
16794             return;
16795         }
16796         
16797         this.page = 0;
16798         this.loadNext = false;
16799         
16800         if(this.isExpanded()){
16801             this.collapse();
16802             if (!this.blockFocus) {
16803                 this.inputEl().focus();
16804             }
16805             
16806         }else {
16807             this.hasFocus = true;
16808             if(this.triggerAction == 'all') {
16809                 this.doQuery(this.allQuery, true);
16810             } else {
16811                 this.doQuery(this.getRawValue());
16812             }
16813             if (!this.blockFocus) {
16814                 this.inputEl().focus();
16815             }
16816         }
16817     },
16818     
16819     onTickableTriggerClick : function(e)
16820     {
16821         if(this.disabled){
16822             return;
16823         }
16824         
16825         this.page = 0;
16826         this.loadNext = false;
16827         this.hasFocus = true;
16828         
16829         if(this.triggerAction == 'all') {
16830             this.doQuery(this.allQuery, true);
16831         } else {
16832             this.doQuery(this.getRawValue());
16833         }
16834     },
16835     
16836     onSearchFieldClick : function(e)
16837     {
16838         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16839             this.onTickableFooterButtonClick(e, false, false);
16840             return;
16841         }
16842         
16843         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16844             return;
16845         }
16846         
16847         this.page = 0;
16848         this.loadNext = false;
16849         this.hasFocus = true;
16850         
16851         if(this.triggerAction == 'all') {
16852             this.doQuery(this.allQuery, true);
16853         } else {
16854             this.doQuery(this.getRawValue());
16855         }
16856     },
16857     
16858     listKeyPress : function(e)
16859     {
16860         //Roo.log('listkeypress');
16861         // scroll to first matching element based on key pres..
16862         if (e.isSpecialKey()) {
16863             return false;
16864         }
16865         var k = String.fromCharCode(e.getKey()).toUpperCase();
16866         //Roo.log(k);
16867         var match  = false;
16868         var csel = this.view.getSelectedNodes();
16869         var cselitem = false;
16870         if (csel.length) {
16871             var ix = this.view.indexOf(csel[0]);
16872             cselitem  = this.store.getAt(ix);
16873             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16874                 cselitem = false;
16875             }
16876             
16877         }
16878         
16879         this.store.each(function(v) { 
16880             if (cselitem) {
16881                 // start at existing selection.
16882                 if (cselitem.id == v.id) {
16883                     cselitem = false;
16884                 }
16885                 return true;
16886             }
16887                 
16888             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16889                 match = this.store.indexOf(v);
16890                 return false;
16891             }
16892             return true;
16893         }, this);
16894         
16895         if (match === false) {
16896             return true; // no more action?
16897         }
16898         // scroll to?
16899         this.view.select(match);
16900         var sn = Roo.get(this.view.getSelectedNodes()[0]);
16901         sn.scrollIntoView(sn.dom.parentNode, false);
16902     },
16903     
16904     onViewScroll : function(e, t){
16905         
16906         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){
16907             return;
16908         }
16909         
16910         this.hasQuery = true;
16911         
16912         this.loading = this.list.select('.loading', true).first();
16913         
16914         if(this.loading === null){
16915             this.list.createChild({
16916                 tag: 'div',
16917                 cls: 'loading roo-select2-more-results roo-select2-active',
16918                 html: 'Loading more results...'
16919             });
16920             
16921             this.loading = this.list.select('.loading', true).first();
16922             
16923             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16924             
16925             this.loading.hide();
16926         }
16927         
16928         this.loading.show();
16929         
16930         var _combo = this;
16931         
16932         this.page++;
16933         this.loadNext = true;
16934         
16935         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16936         
16937         return;
16938     },
16939     
16940     addItem : function(o)
16941     {   
16942         var dv = ''; // display value
16943         
16944         if (this.displayField) {
16945             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16946         } else {
16947             // this is an error condition!!!
16948             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16949         }
16950         
16951         if(!dv.length){
16952             return;
16953         }
16954         
16955         var choice = this.choices.createChild({
16956             tag: 'li',
16957             cls: 'roo-select2-search-choice',
16958             cn: [
16959                 {
16960                     tag: 'div',
16961                     html: dv
16962                 },
16963                 {
16964                     tag: 'a',
16965                     href: '#',
16966                     cls: 'roo-select2-search-choice-close fa fa-times',
16967                     tabindex: '-1'
16968                 }
16969             ]
16970             
16971         }, this.searchField);
16972         
16973         var close = choice.select('a.roo-select2-search-choice-close', true).first();
16974         
16975         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16976         
16977         this.item.push(o);
16978         
16979         this.lastData = o;
16980         
16981         this.syncValue();
16982         
16983         this.inputEl().dom.value = '';
16984         
16985         this.validate();
16986     },
16987     
16988     onRemoveItem : function(e, _self, o)
16989     {
16990         e.preventDefault();
16991         
16992         this.lastItem = Roo.apply([], this.item);
16993         
16994         var index = this.item.indexOf(o.data) * 1;
16995         
16996         if( index < 0){
16997             Roo.log('not this item?!');
16998             return;
16999         }
17000         
17001         this.item.splice(index, 1);
17002         o.item.remove();
17003         
17004         this.syncValue();
17005         
17006         this.fireEvent('remove', this, e);
17007         
17008         this.validate();
17009         
17010     },
17011     
17012     syncValue : function()
17013     {
17014         if(!this.item.length){
17015             this.clearValue();
17016             return;
17017         }
17018             
17019         var value = [];
17020         var _this = this;
17021         Roo.each(this.item, function(i){
17022             if(_this.valueField){
17023                 value.push(i[_this.valueField]);
17024                 return;
17025             }
17026
17027             value.push(i);
17028         });
17029
17030         this.value = value.join(',');
17031
17032         if(this.hiddenField){
17033             this.hiddenField.dom.value = this.value;
17034         }
17035         
17036         this.store.fireEvent("datachanged", this.store);
17037         
17038         this.validate();
17039     },
17040     
17041     clearItem : function()
17042     {
17043         if(!this.multiple){
17044             return;
17045         }
17046         
17047         this.item = [];
17048         
17049         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17050            c.remove();
17051         });
17052         
17053         this.syncValue();
17054         
17055         this.validate();
17056         
17057         if(this.tickable && !Roo.isTouch){
17058             this.view.refresh();
17059         }
17060     },
17061     
17062     inputEl: function ()
17063     {
17064         if(Roo.isIOS && this.useNativeIOS){
17065             return this.el.select('select.roo-ios-select', true).first();
17066         }
17067         
17068         if(Roo.isTouch && this.mobileTouchView){
17069             return this.el.select('input.form-control',true).first();
17070         }
17071         
17072         if(this.tickable){
17073             return this.searchField;
17074         }
17075         
17076         return this.el.select('input.form-control',true).first();
17077     },
17078     
17079     onTickableFooterButtonClick : function(e, btn, el)
17080     {
17081         e.preventDefault();
17082         
17083         this.lastItem = Roo.apply([], this.item);
17084         
17085         if(btn && btn.name == 'cancel'){
17086             this.tickItems = Roo.apply([], this.item);
17087             this.collapse();
17088             return;
17089         }
17090         
17091         this.clearItem();
17092         
17093         var _this = this;
17094         
17095         Roo.each(this.tickItems, function(o){
17096             _this.addItem(o);
17097         });
17098         
17099         this.collapse();
17100         
17101     },
17102     
17103     validate : function()
17104     {
17105         if(this.getVisibilityEl().hasClass('hidden')){
17106             return true;
17107         }
17108         
17109         var v = this.getRawValue();
17110         
17111         if(this.multiple){
17112             v = this.getValue();
17113         }
17114         
17115         if(this.disabled || this.allowBlank || v.length){
17116             this.markValid();
17117             return true;
17118         }
17119         
17120         this.markInvalid();
17121         return false;
17122     },
17123     
17124     tickableInputEl : function()
17125     {
17126         if(!this.tickable || !this.editable){
17127             return this.inputEl();
17128         }
17129         
17130         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17131     },
17132     
17133     
17134     getAutoCreateTouchView : function()
17135     {
17136         var id = Roo.id();
17137         
17138         var cfg = {
17139             cls: 'form-group' //input-group
17140         };
17141         
17142         var input =  {
17143             tag: 'input',
17144             id : id,
17145             type : this.inputType,
17146             cls : 'form-control x-combo-noedit',
17147             autocomplete: 'new-password',
17148             placeholder : this.placeholder || '',
17149             readonly : true
17150         };
17151         
17152         if (this.name) {
17153             input.name = this.name;
17154         }
17155         
17156         if (this.size) {
17157             input.cls += ' input-' + this.size;
17158         }
17159         
17160         if (this.disabled) {
17161             input.disabled = true;
17162         }
17163         
17164         var inputblock = {
17165             cls : 'roo-combobox-wrap',
17166             cn : [
17167                 input
17168             ]
17169         };
17170         
17171         if(this.before){
17172             inputblock.cls += ' input-group';
17173             
17174             inputblock.cn.unshift({
17175                 tag :'span',
17176                 cls : 'input-group-addon input-group-prepend input-group-text',
17177                 html : this.before
17178             });
17179         }
17180         
17181         if(this.removable && !this.multiple){
17182             inputblock.cls += ' roo-removable';
17183             
17184             inputblock.cn.push({
17185                 tag: 'button',
17186                 html : 'x',
17187                 cls : 'roo-combo-removable-btn close'
17188             });
17189         }
17190
17191         if(this.hasFeedback && !this.allowBlank){
17192             
17193             inputblock.cls += ' has-feedback';
17194             
17195             inputblock.cn.push({
17196                 tag: 'span',
17197                 cls: 'glyphicon form-control-feedback'
17198             });
17199             
17200         }
17201         
17202         if (this.after) {
17203             
17204             inputblock.cls += (this.before) ? '' : ' input-group';
17205             
17206             inputblock.cn.push({
17207                 tag :'span',
17208                 cls : 'input-group-addon input-group-append input-group-text',
17209                 html : this.after
17210             });
17211         }
17212
17213         
17214         var ibwrap = inputblock;
17215         
17216         if(this.multiple){
17217             ibwrap = {
17218                 tag: 'ul',
17219                 cls: 'roo-select2-choices',
17220                 cn:[
17221                     {
17222                         tag: 'li',
17223                         cls: 'roo-select2-search-field',
17224                         cn: [
17225
17226                             inputblock
17227                         ]
17228                     }
17229                 ]
17230             };
17231         
17232             
17233         }
17234         
17235         var combobox = {
17236             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17237             cn: [
17238                 {
17239                     tag: 'input',
17240                     type : 'hidden',
17241                     cls: 'form-hidden-field'
17242                 },
17243                 ibwrap
17244             ]
17245         };
17246         
17247         if(!this.multiple && this.showToggleBtn){
17248             
17249             var caret = {
17250                 cls: 'caret'
17251             };
17252             
17253             if (this.caret != false) {
17254                 caret = {
17255                      tag: 'i',
17256                      cls: 'fa fa-' + this.caret
17257                 };
17258                 
17259             }
17260             
17261             combobox.cn.push({
17262                 tag :'span',
17263                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17264                 cn : [
17265                     Roo.bootstrap.version == 3 ? caret : '',
17266                     {
17267                         tag: 'span',
17268                         cls: 'combobox-clear',
17269                         cn  : [
17270                             {
17271                                 tag : 'i',
17272                                 cls: 'icon-remove'
17273                             }
17274                         ]
17275                     }
17276                 ]
17277
17278             })
17279         }
17280         
17281         if(this.multiple){
17282             combobox.cls += ' roo-select2-container-multi';
17283         }
17284         
17285         var align = this.labelAlign || this.parentLabelAlign();
17286         
17287         if (align ==='left' && this.fieldLabel.length) {
17288
17289             cfg.cn = [
17290                 {
17291                    tag : 'i',
17292                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17293                    tooltip : 'This field is required'
17294                 },
17295                 {
17296                     tag: 'label',
17297                     cls : 'control-label col-form-label',
17298                     html : this.fieldLabel
17299
17300                 },
17301                 {
17302                     cls : 'roo-combobox-wrap ', 
17303                     cn: [
17304                         combobox
17305                     ]
17306                 }
17307             ];
17308             
17309             var labelCfg = cfg.cn[1];
17310             var contentCfg = cfg.cn[2];
17311             
17312
17313             if(this.indicatorpos == 'right'){
17314                 cfg.cn = [
17315                     {
17316                         tag: 'label',
17317                         'for' :  id,
17318                         cls : 'control-label col-form-label',
17319                         cn : [
17320                             {
17321                                 tag : 'span',
17322                                 html : this.fieldLabel
17323                             },
17324                             {
17325                                 tag : 'i',
17326                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17327                                 tooltip : 'This field is required'
17328                             }
17329                         ]
17330                     },
17331                     {
17332                         cls : "roo-combobox-wrap ",
17333                         cn: [
17334                             combobox
17335                         ]
17336                     }
17337
17338                 ];
17339                 
17340                 labelCfg = cfg.cn[0];
17341                 contentCfg = cfg.cn[1];
17342             }
17343             
17344            
17345             
17346             if(this.labelWidth > 12){
17347                 labelCfg.style = "width: " + this.labelWidth + 'px';
17348             }
17349            
17350             if(this.labelWidth < 13 && this.labelmd == 0){
17351                 this.labelmd = this.labelWidth;
17352             }
17353             
17354             if(this.labellg > 0){
17355                 labelCfg.cls += ' col-lg-' + this.labellg;
17356                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17357             }
17358             
17359             if(this.labelmd > 0){
17360                 labelCfg.cls += ' col-md-' + this.labelmd;
17361                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17362             }
17363             
17364             if(this.labelsm > 0){
17365                 labelCfg.cls += ' col-sm-' + this.labelsm;
17366                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17367             }
17368             
17369             if(this.labelxs > 0){
17370                 labelCfg.cls += ' col-xs-' + this.labelxs;
17371                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17372             }
17373                 
17374                 
17375         } else if ( this.fieldLabel.length) {
17376             cfg.cn = [
17377                 {
17378                    tag : 'i',
17379                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17380                    tooltip : 'This field is required'
17381                 },
17382                 {
17383                     tag: 'label',
17384                     cls : 'control-label',
17385                     html : this.fieldLabel
17386
17387                 },
17388                 {
17389                     cls : '', 
17390                     cn: [
17391                         combobox
17392                     ]
17393                 }
17394             ];
17395             
17396             if(this.indicatorpos == 'right'){
17397                 cfg.cn = [
17398                     {
17399                         tag: 'label',
17400                         cls : 'control-label',
17401                         html : this.fieldLabel,
17402                         cn : [
17403                             {
17404                                tag : 'i',
17405                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17406                                tooltip : 'This field is required'
17407                             }
17408                         ]
17409                     },
17410                     {
17411                         cls : '', 
17412                         cn: [
17413                             combobox
17414                         ]
17415                     }
17416                 ];
17417             }
17418         } else {
17419             cfg.cn = combobox;    
17420         }
17421         
17422         
17423         var settings = this;
17424         
17425         ['xs','sm','md','lg'].map(function(size){
17426             if (settings[size]) {
17427                 cfg.cls += ' col-' + size + '-' + settings[size];
17428             }
17429         });
17430         
17431         return cfg;
17432     },
17433     
17434     initTouchView : function()
17435     {
17436         this.renderTouchView();
17437         
17438         this.touchViewEl.on('scroll', function(){
17439             this.el.dom.scrollTop = 0;
17440         }, this);
17441         
17442         this.originalValue = this.getValue();
17443         
17444         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17445         
17446         this.inputEl().on("click", this.showTouchView, this);
17447         if (this.triggerEl) {
17448             this.triggerEl.on("click", this.showTouchView, this);
17449         }
17450         
17451         
17452         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17453         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17454         
17455         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17456         
17457         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17458         this.store.on('load', this.onTouchViewLoad, this);
17459         this.store.on('loadexception', this.onTouchViewLoadException, this);
17460         
17461         if(this.hiddenName){
17462             
17463             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17464             
17465             this.hiddenField.dom.value =
17466                 this.hiddenValue !== undefined ? this.hiddenValue :
17467                 this.value !== undefined ? this.value : '';
17468         
17469             this.el.dom.removeAttribute('name');
17470             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17471         }
17472         
17473         if(this.multiple){
17474             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17475             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17476         }
17477         
17478         if(this.removable && !this.multiple){
17479             var close = this.closeTriggerEl();
17480             if(close){
17481                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17482                 close.on('click', this.removeBtnClick, this, close);
17483             }
17484         }
17485         /*
17486          * fix the bug in Safari iOS8
17487          */
17488         this.inputEl().on("focus", function(e){
17489             document.activeElement.blur();
17490         }, this);
17491         
17492         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17493         
17494         return;
17495         
17496         
17497     },
17498     
17499     renderTouchView : function()
17500     {
17501         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17502         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17503         
17504         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17505         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17506         
17507         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17508         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17509         this.touchViewBodyEl.setStyle('overflow', 'auto');
17510         
17511         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17512         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17513         
17514         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17515         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17516         
17517     },
17518     
17519     showTouchView : function()
17520     {
17521         if(this.disabled){
17522             return;
17523         }
17524         
17525         this.touchViewHeaderEl.hide();
17526
17527         if(this.modalTitle.length){
17528             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17529             this.touchViewHeaderEl.show();
17530         }
17531
17532         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17533         this.touchViewEl.show();
17534
17535         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17536         
17537         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17538         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17539
17540         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17541
17542         if(this.modalTitle.length){
17543             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17544         }
17545         
17546         this.touchViewBodyEl.setHeight(bodyHeight);
17547
17548         if(this.animate){
17549             var _this = this;
17550             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17551         }else{
17552             this.touchViewEl.addClass(['in','show']);
17553         }
17554         
17555         if(this._touchViewMask){
17556             Roo.get(document.body).addClass("x-body-masked");
17557             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17558             this._touchViewMask.setStyle('z-index', 10000);
17559             this._touchViewMask.addClass('show');
17560         }
17561         
17562         this.doTouchViewQuery();
17563         
17564     },
17565     
17566     hideTouchView : function()
17567     {
17568         this.touchViewEl.removeClass(['in','show']);
17569
17570         if(this.animate){
17571             var _this = this;
17572             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17573         }else{
17574             this.touchViewEl.setStyle('display', 'none');
17575         }
17576         
17577         if(this._touchViewMask){
17578             this._touchViewMask.removeClass('show');
17579             Roo.get(document.body).removeClass("x-body-masked");
17580         }
17581     },
17582     
17583     setTouchViewValue : function()
17584     {
17585         if(this.multiple){
17586             this.clearItem();
17587         
17588             var _this = this;
17589
17590             Roo.each(this.tickItems, function(o){
17591                 this.addItem(o);
17592             }, this);
17593         }
17594         
17595         this.hideTouchView();
17596     },
17597     
17598     doTouchViewQuery : function()
17599     {
17600         var qe = {
17601             query: '',
17602             forceAll: true,
17603             combo: this,
17604             cancel:false
17605         };
17606         
17607         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17608             return false;
17609         }
17610         
17611         if(!this.alwaysQuery || this.mode == 'local'){
17612             this.onTouchViewLoad();
17613             return;
17614         }
17615         
17616         this.store.load();
17617     },
17618     
17619     onTouchViewBeforeLoad : function(combo,opts)
17620     {
17621         return;
17622     },
17623
17624     // private
17625     onTouchViewLoad : function()
17626     {
17627         if(this.store.getCount() < 1){
17628             this.onTouchViewEmptyResults();
17629             return;
17630         }
17631         
17632         this.clearTouchView();
17633         
17634         var rawValue = this.getRawValue();
17635         
17636         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17637         
17638         this.tickItems = [];
17639         
17640         this.store.data.each(function(d, rowIndex){
17641             var row = this.touchViewListGroup.createChild(template);
17642             
17643             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17644                 row.addClass(d.data.cls);
17645             }
17646             
17647             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17648                 var cfg = {
17649                     data : d.data,
17650                     html : d.data[this.displayField]
17651                 };
17652                 
17653                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17654                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17655                 }
17656             }
17657             row.removeClass('selected');
17658             if(!this.multiple && this.valueField &&
17659                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17660             {
17661                 // radio buttons..
17662                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17663                 row.addClass('selected');
17664             }
17665             
17666             if(this.multiple && this.valueField &&
17667                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17668             {
17669                 
17670                 // checkboxes...
17671                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17672                 this.tickItems.push(d.data);
17673             }
17674             
17675             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17676             
17677         }, this);
17678         
17679         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17680         
17681         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17682
17683         if(this.modalTitle.length){
17684             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17685         }
17686
17687         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17688         
17689         if(this.mobile_restrict_height && listHeight < bodyHeight){
17690             this.touchViewBodyEl.setHeight(listHeight);
17691         }
17692         
17693         var _this = this;
17694         
17695         if(firstChecked && listHeight > bodyHeight){
17696             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17697         }
17698         
17699     },
17700     
17701     onTouchViewLoadException : function()
17702     {
17703         this.hideTouchView();
17704     },
17705     
17706     onTouchViewEmptyResults : function()
17707     {
17708         this.clearTouchView();
17709         
17710         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17711         
17712         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17713         
17714     },
17715     
17716     clearTouchView : function()
17717     {
17718         this.touchViewListGroup.dom.innerHTML = '';
17719     },
17720     
17721     onTouchViewClick : function(e, el, o)
17722     {
17723         e.preventDefault();
17724         
17725         var row = o.row;
17726         var rowIndex = o.rowIndex;
17727         
17728         var r = this.store.getAt(rowIndex);
17729         
17730         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17731             
17732             if(!this.multiple){
17733                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17734                     c.dom.removeAttribute('checked');
17735                 }, this);
17736
17737                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17738
17739                 this.setFromData(r.data);
17740
17741                 var close = this.closeTriggerEl();
17742
17743                 if(close){
17744                     close.show();
17745                 }
17746
17747                 this.hideTouchView();
17748
17749                 this.fireEvent('select', this, r, rowIndex);
17750
17751                 return;
17752             }
17753
17754             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17755                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17756                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17757                 return;
17758             }
17759
17760             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17761             this.addItem(r.data);
17762             this.tickItems.push(r.data);
17763         }
17764     },
17765     
17766     getAutoCreateNativeIOS : function()
17767     {
17768         var cfg = {
17769             cls: 'form-group' //input-group,
17770         };
17771         
17772         var combobox =  {
17773             tag: 'select',
17774             cls : 'roo-ios-select'
17775         };
17776         
17777         if (this.name) {
17778             combobox.name = this.name;
17779         }
17780         
17781         if (this.disabled) {
17782             combobox.disabled = true;
17783         }
17784         
17785         var settings = this;
17786         
17787         ['xs','sm','md','lg'].map(function(size){
17788             if (settings[size]) {
17789                 cfg.cls += ' col-' + size + '-' + settings[size];
17790             }
17791         });
17792         
17793         cfg.cn = combobox;
17794         
17795         return cfg;
17796         
17797     },
17798     
17799     initIOSView : function()
17800     {
17801         this.store.on('load', this.onIOSViewLoad, this);
17802         
17803         return;
17804     },
17805     
17806     onIOSViewLoad : function()
17807     {
17808         if(this.store.getCount() < 1){
17809             return;
17810         }
17811         
17812         this.clearIOSView();
17813         
17814         if(this.allowBlank) {
17815             
17816             var default_text = '-- SELECT --';
17817             
17818             if(this.placeholder.length){
17819                 default_text = this.placeholder;
17820             }
17821             
17822             if(this.emptyTitle.length){
17823                 default_text += ' - ' + this.emptyTitle + ' -';
17824             }
17825             
17826             var opt = this.inputEl().createChild({
17827                 tag: 'option',
17828                 value : 0,
17829                 html : default_text
17830             });
17831             
17832             var o = {};
17833             o[this.valueField] = 0;
17834             o[this.displayField] = default_text;
17835             
17836             this.ios_options.push({
17837                 data : o,
17838                 el : opt
17839             });
17840             
17841         }
17842         
17843         this.store.data.each(function(d, rowIndex){
17844             
17845             var html = '';
17846             
17847             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17848                 html = d.data[this.displayField];
17849             }
17850             
17851             var value = '';
17852             
17853             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17854                 value = d.data[this.valueField];
17855             }
17856             
17857             var option = {
17858                 tag: 'option',
17859                 value : value,
17860                 html : html
17861             };
17862             
17863             if(this.value == d.data[this.valueField]){
17864                 option['selected'] = true;
17865             }
17866             
17867             var opt = this.inputEl().createChild(option);
17868             
17869             this.ios_options.push({
17870                 data : d.data,
17871                 el : opt
17872             });
17873             
17874         }, this);
17875         
17876         this.inputEl().on('change', function(){
17877            this.fireEvent('select', this);
17878         }, this);
17879         
17880     },
17881     
17882     clearIOSView: function()
17883     {
17884         this.inputEl().dom.innerHTML = '';
17885         
17886         this.ios_options = [];
17887     },
17888     
17889     setIOSValue: function(v)
17890     {
17891         this.value = v;
17892         
17893         if(!this.ios_options){
17894             return;
17895         }
17896         
17897         Roo.each(this.ios_options, function(opts){
17898            
17899            opts.el.dom.removeAttribute('selected');
17900            
17901            if(opts.data[this.valueField] != v){
17902                return;
17903            }
17904            
17905            opts.el.dom.setAttribute('selected', true);
17906            
17907         }, this);
17908     }
17909
17910     /** 
17911     * @cfg {Boolean} grow 
17912     * @hide 
17913     */
17914     /** 
17915     * @cfg {Number} growMin 
17916     * @hide 
17917     */
17918     /** 
17919     * @cfg {Number} growMax 
17920     * @hide 
17921     */
17922     /**
17923      * @hide
17924      * @method autoSize
17925      */
17926 });
17927
17928 Roo.apply(Roo.bootstrap.ComboBox,  {
17929     
17930     header : {
17931         tag: 'div',
17932         cls: 'modal-header',
17933         cn: [
17934             {
17935                 tag: 'h4',
17936                 cls: 'modal-title'
17937             }
17938         ]
17939     },
17940     
17941     body : {
17942         tag: 'div',
17943         cls: 'modal-body',
17944         cn: [
17945             {
17946                 tag: 'ul',
17947                 cls: 'list-group'
17948             }
17949         ]
17950     },
17951     
17952     listItemRadio : {
17953         tag: 'li',
17954         cls: 'list-group-item',
17955         cn: [
17956             {
17957                 tag: 'span',
17958                 cls: 'roo-combobox-list-group-item-value'
17959             },
17960             {
17961                 tag: 'div',
17962                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17963                 cn: [
17964                     {
17965                         tag: 'input',
17966                         type: 'radio'
17967                     },
17968                     {
17969                         tag: 'label'
17970                     }
17971                 ]
17972             }
17973         ]
17974     },
17975     
17976     listItemCheckbox : {
17977         tag: 'li',
17978         cls: 'list-group-item',
17979         cn: [
17980             {
17981                 tag: 'span',
17982                 cls: 'roo-combobox-list-group-item-value'
17983             },
17984             {
17985                 tag: 'div',
17986                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17987                 cn: [
17988                     {
17989                         tag: 'input',
17990                         type: 'checkbox'
17991                     },
17992                     {
17993                         tag: 'label'
17994                     }
17995                 ]
17996             }
17997         ]
17998     },
17999     
18000     emptyResult : {
18001         tag: 'div',
18002         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18003     },
18004     
18005     footer : {
18006         tag: 'div',
18007         cls: 'modal-footer',
18008         cn: [
18009             {
18010                 tag: 'div',
18011                 cls: 'row',
18012                 cn: [
18013                     {
18014                         tag: 'div',
18015                         cls: 'col-xs-6 text-left',
18016                         cn: {
18017                             tag: 'button',
18018                             cls: 'btn btn-danger roo-touch-view-cancel',
18019                             html: 'Cancel'
18020                         }
18021                     },
18022                     {
18023                         tag: 'div',
18024                         cls: 'col-xs-6 text-right',
18025                         cn: {
18026                             tag: 'button',
18027                             cls: 'btn btn-success roo-touch-view-ok',
18028                             html: 'OK'
18029                         }
18030                     }
18031                 ]
18032             }
18033         ]
18034         
18035     }
18036 });
18037
18038 Roo.apply(Roo.bootstrap.ComboBox,  {
18039     
18040     touchViewTemplate : {
18041         tag: 'div',
18042         cls: 'modal fade roo-combobox-touch-view',
18043         cn: [
18044             {
18045                 tag: 'div',
18046                 cls: 'modal-dialog',
18047                 style : 'position:fixed', // we have to fix position....
18048                 cn: [
18049                     {
18050                         tag: 'div',
18051                         cls: 'modal-content',
18052                         cn: [
18053                             Roo.bootstrap.ComboBox.header,
18054                             Roo.bootstrap.ComboBox.body,
18055                             Roo.bootstrap.ComboBox.footer
18056                         ]
18057                     }
18058                 ]
18059             }
18060         ]
18061     }
18062 });/*
18063  * Based on:
18064  * Ext JS Library 1.1.1
18065  * Copyright(c) 2006-2007, Ext JS, LLC.
18066  *
18067  * Originally Released Under LGPL - original licence link has changed is not relivant.
18068  *
18069  * Fork - LGPL
18070  * <script type="text/javascript">
18071  */
18072
18073 /**
18074  * @class Roo.View
18075  * @extends Roo.util.Observable
18076  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18077  * This class also supports single and multi selection modes. <br>
18078  * Create a data model bound view:
18079  <pre><code>
18080  var store = new Roo.data.Store(...);
18081
18082  var view = new Roo.View({
18083     el : "my-element",
18084     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18085  
18086     singleSelect: true,
18087     selectedClass: "ydataview-selected",
18088     store: store
18089  });
18090
18091  // listen for node click?
18092  view.on("click", function(vw, index, node, e){
18093  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18094  });
18095
18096  // load XML data
18097  dataModel.load("foobar.xml");
18098  </code></pre>
18099  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18100  * <br><br>
18101  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18102  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18103  * 
18104  * Note: old style constructor is still suported (container, template, config)
18105  * 
18106  * @constructor
18107  * Create a new View
18108  * @param {Object} config The config object
18109  * 
18110  */
18111 Roo.View = function(config, depreciated_tpl, depreciated_config){
18112     
18113     this.parent = false;
18114     
18115     if (typeof(depreciated_tpl) == 'undefined') {
18116         // new way.. - universal constructor.
18117         Roo.apply(this, config);
18118         this.el  = Roo.get(this.el);
18119     } else {
18120         // old format..
18121         this.el  = Roo.get(config);
18122         this.tpl = depreciated_tpl;
18123         Roo.apply(this, depreciated_config);
18124     }
18125     this.wrapEl  = this.el.wrap().wrap();
18126     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18127     
18128     
18129     if(typeof(this.tpl) == "string"){
18130         this.tpl = new Roo.Template(this.tpl);
18131     } else {
18132         // support xtype ctors..
18133         this.tpl = new Roo.factory(this.tpl, Roo);
18134     }
18135     
18136     
18137     this.tpl.compile();
18138     
18139     /** @private */
18140     this.addEvents({
18141         /**
18142          * @event beforeclick
18143          * Fires before a click is processed. Returns false to cancel the default action.
18144          * @param {Roo.View} this
18145          * @param {Number} index The index of the target node
18146          * @param {HTMLElement} node The target node
18147          * @param {Roo.EventObject} e The raw event object
18148          */
18149             "beforeclick" : true,
18150         /**
18151          * @event click
18152          * Fires when a template node is clicked.
18153          * @param {Roo.View} this
18154          * @param {Number} index The index of the target node
18155          * @param {HTMLElement} node The target node
18156          * @param {Roo.EventObject} e The raw event object
18157          */
18158             "click" : true,
18159         /**
18160          * @event dblclick
18161          * Fires when a template node is double clicked.
18162          * @param {Roo.View} this
18163          * @param {Number} index The index of the target node
18164          * @param {HTMLElement} node The target node
18165          * @param {Roo.EventObject} e The raw event object
18166          */
18167             "dblclick" : true,
18168         /**
18169          * @event contextmenu
18170          * Fires when a template node is right clicked.
18171          * @param {Roo.View} this
18172          * @param {Number} index The index of the target node
18173          * @param {HTMLElement} node The target node
18174          * @param {Roo.EventObject} e The raw event object
18175          */
18176             "contextmenu" : true,
18177         /**
18178          * @event selectionchange
18179          * Fires when the selected nodes change.
18180          * @param {Roo.View} this
18181          * @param {Array} selections Array of the selected nodes
18182          */
18183             "selectionchange" : true,
18184     
18185         /**
18186          * @event beforeselect
18187          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18188          * @param {Roo.View} this
18189          * @param {HTMLElement} node The node to be selected
18190          * @param {Array} selections Array of currently selected nodes
18191          */
18192             "beforeselect" : true,
18193         /**
18194          * @event preparedata
18195          * Fires on every row to render, to allow you to change the data.
18196          * @param {Roo.View} this
18197          * @param {Object} data to be rendered (change this)
18198          */
18199           "preparedata" : true
18200           
18201           
18202         });
18203
18204
18205
18206     this.el.on({
18207         "click": this.onClick,
18208         "dblclick": this.onDblClick,
18209         "contextmenu": this.onContextMenu,
18210         scope:this
18211     });
18212
18213     this.selections = [];
18214     this.nodes = [];
18215     this.cmp = new Roo.CompositeElementLite([]);
18216     if(this.store){
18217         this.store = Roo.factory(this.store, Roo.data);
18218         this.setStore(this.store, true);
18219     }
18220     
18221     if ( this.footer && this.footer.xtype) {
18222            
18223          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18224         
18225         this.footer.dataSource = this.store;
18226         this.footer.container = fctr;
18227         this.footer = Roo.factory(this.footer, Roo);
18228         fctr.insertFirst(this.el);
18229         
18230         // this is a bit insane - as the paging toolbar seems to detach the el..
18231 //        dom.parentNode.parentNode.parentNode
18232          // they get detached?
18233     }
18234     
18235     
18236     Roo.View.superclass.constructor.call(this);
18237     
18238     
18239 };
18240
18241 Roo.extend(Roo.View, Roo.util.Observable, {
18242     
18243      /**
18244      * @cfg {Roo.data.Store} store Data store to load data from.
18245      */
18246     store : false,
18247     
18248     /**
18249      * @cfg {String|Roo.Element} el The container element.
18250      */
18251     el : '',
18252     
18253     /**
18254      * @cfg {String|Roo.Template} tpl The template used by this View 
18255      */
18256     tpl : false,
18257     /**
18258      * @cfg {String} dataName the named area of the template to use as the data area
18259      *                          Works with domtemplates roo-name="name"
18260      */
18261     dataName: false,
18262     /**
18263      * @cfg {String} selectedClass The css class to add to selected nodes
18264      */
18265     selectedClass : "x-view-selected",
18266      /**
18267      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18268      */
18269     emptyText : "",
18270     
18271     /**
18272      * @cfg {String} text to display on mask (default Loading)
18273      */
18274     mask : false,
18275     /**
18276      * @cfg {Boolean} multiSelect Allow multiple selection
18277      */
18278     multiSelect : false,
18279     /**
18280      * @cfg {Boolean} singleSelect Allow single selection
18281      */
18282     singleSelect:  false,
18283     
18284     /**
18285      * @cfg {Boolean} toggleSelect - selecting 
18286      */
18287     toggleSelect : false,
18288     
18289     /**
18290      * @cfg {Boolean} tickable - selecting 
18291      */
18292     tickable : false,
18293     
18294     /**
18295      * Returns the element this view is bound to.
18296      * @return {Roo.Element}
18297      */
18298     getEl : function(){
18299         return this.wrapEl;
18300     },
18301     
18302     
18303
18304     /**
18305      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18306      */
18307     refresh : function(){
18308         //Roo.log('refresh');
18309         var t = this.tpl;
18310         
18311         // if we are using something like 'domtemplate', then
18312         // the what gets used is:
18313         // t.applySubtemplate(NAME, data, wrapping data..)
18314         // the outer template then get' applied with
18315         //     the store 'extra data'
18316         // and the body get's added to the
18317         //      roo-name="data" node?
18318         //      <span class='roo-tpl-{name}'></span> ?????
18319         
18320         
18321         
18322         this.clearSelections();
18323         this.el.update("");
18324         var html = [];
18325         var records = this.store.getRange();
18326         if(records.length < 1) {
18327             
18328             // is this valid??  = should it render a template??
18329             
18330             this.el.update(this.emptyText);
18331             return;
18332         }
18333         var el = this.el;
18334         if (this.dataName) {
18335             this.el.update(t.apply(this.store.meta)); //????
18336             el = this.el.child('.roo-tpl-' + this.dataName);
18337         }
18338         
18339         for(var i = 0, len = records.length; i < len; i++){
18340             var data = this.prepareData(records[i].data, i, records[i]);
18341             this.fireEvent("preparedata", this, data, i, records[i]);
18342             
18343             var d = Roo.apply({}, data);
18344             
18345             if(this.tickable){
18346                 Roo.apply(d, {'roo-id' : Roo.id()});
18347                 
18348                 var _this = this;
18349             
18350                 Roo.each(this.parent.item, function(item){
18351                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18352                         return;
18353                     }
18354                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18355                 });
18356             }
18357             
18358             html[html.length] = Roo.util.Format.trim(
18359                 this.dataName ?
18360                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18361                     t.apply(d)
18362             );
18363         }
18364         
18365         
18366         
18367         el.update(html.join(""));
18368         this.nodes = el.dom.childNodes;
18369         this.updateIndexes(0);
18370     },
18371     
18372
18373     /**
18374      * Function to override to reformat the data that is sent to
18375      * the template for each node.
18376      * DEPRICATED - use the preparedata event handler.
18377      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18378      * a JSON object for an UpdateManager bound view).
18379      */
18380     prepareData : function(data, index, record)
18381     {
18382         this.fireEvent("preparedata", this, data, index, record);
18383         return data;
18384     },
18385
18386     onUpdate : function(ds, record){
18387         // Roo.log('on update');   
18388         this.clearSelections();
18389         var index = this.store.indexOf(record);
18390         var n = this.nodes[index];
18391         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18392         n.parentNode.removeChild(n);
18393         this.updateIndexes(index, index);
18394     },
18395
18396     
18397     
18398 // --------- FIXME     
18399     onAdd : function(ds, records, index)
18400     {
18401         //Roo.log(['on Add', ds, records, index] );        
18402         this.clearSelections();
18403         if(this.nodes.length == 0){
18404             this.refresh();
18405             return;
18406         }
18407         var n = this.nodes[index];
18408         for(var i = 0, len = records.length; i < len; i++){
18409             var d = this.prepareData(records[i].data, i, records[i]);
18410             if(n){
18411                 this.tpl.insertBefore(n, d);
18412             }else{
18413                 
18414                 this.tpl.append(this.el, d);
18415             }
18416         }
18417         this.updateIndexes(index);
18418     },
18419
18420     onRemove : function(ds, record, index){
18421        // Roo.log('onRemove');
18422         this.clearSelections();
18423         var el = this.dataName  ?
18424             this.el.child('.roo-tpl-' + this.dataName) :
18425             this.el; 
18426         
18427         el.dom.removeChild(this.nodes[index]);
18428         this.updateIndexes(index);
18429     },
18430
18431     /**
18432      * Refresh an individual node.
18433      * @param {Number} index
18434      */
18435     refreshNode : function(index){
18436         this.onUpdate(this.store, this.store.getAt(index));
18437     },
18438
18439     updateIndexes : function(startIndex, endIndex){
18440         var ns = this.nodes;
18441         startIndex = startIndex || 0;
18442         endIndex = endIndex || ns.length - 1;
18443         for(var i = startIndex; i <= endIndex; i++){
18444             ns[i].nodeIndex = i;
18445         }
18446     },
18447
18448     /**
18449      * Changes the data store this view uses and refresh the view.
18450      * @param {Store} store
18451      */
18452     setStore : function(store, initial){
18453         if(!initial && this.store){
18454             this.store.un("datachanged", this.refresh);
18455             this.store.un("add", this.onAdd);
18456             this.store.un("remove", this.onRemove);
18457             this.store.un("update", this.onUpdate);
18458             this.store.un("clear", this.refresh);
18459             this.store.un("beforeload", this.onBeforeLoad);
18460             this.store.un("load", this.onLoad);
18461             this.store.un("loadexception", this.onLoad);
18462         }
18463         if(store){
18464           
18465             store.on("datachanged", this.refresh, this);
18466             store.on("add", this.onAdd, this);
18467             store.on("remove", this.onRemove, this);
18468             store.on("update", this.onUpdate, this);
18469             store.on("clear", this.refresh, this);
18470             store.on("beforeload", this.onBeforeLoad, this);
18471             store.on("load", this.onLoad, this);
18472             store.on("loadexception", this.onLoad, this);
18473         }
18474         
18475         if(store){
18476             this.refresh();
18477         }
18478     },
18479     /**
18480      * onbeforeLoad - masks the loading area.
18481      *
18482      */
18483     onBeforeLoad : function(store,opts)
18484     {
18485          //Roo.log('onBeforeLoad');   
18486         if (!opts.add) {
18487             this.el.update("");
18488         }
18489         this.el.mask(this.mask ? this.mask : "Loading" ); 
18490     },
18491     onLoad : function ()
18492     {
18493         this.el.unmask();
18494     },
18495     
18496
18497     /**
18498      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18499      * @param {HTMLElement} node
18500      * @return {HTMLElement} The template node
18501      */
18502     findItemFromChild : function(node){
18503         var el = this.dataName  ?
18504             this.el.child('.roo-tpl-' + this.dataName,true) :
18505             this.el.dom; 
18506         
18507         if(!node || node.parentNode == el){
18508                     return node;
18509             }
18510             var p = node.parentNode;
18511             while(p && p != el){
18512             if(p.parentNode == el){
18513                 return p;
18514             }
18515             p = p.parentNode;
18516         }
18517             return null;
18518     },
18519
18520     /** @ignore */
18521     onClick : function(e){
18522         var item = this.findItemFromChild(e.getTarget());
18523         if(item){
18524             var index = this.indexOf(item);
18525             if(this.onItemClick(item, index, e) !== false){
18526                 this.fireEvent("click", this, index, item, e);
18527             }
18528         }else{
18529             this.clearSelections();
18530         }
18531     },
18532
18533     /** @ignore */
18534     onContextMenu : function(e){
18535         var item = this.findItemFromChild(e.getTarget());
18536         if(item){
18537             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18538         }
18539     },
18540
18541     /** @ignore */
18542     onDblClick : function(e){
18543         var item = this.findItemFromChild(e.getTarget());
18544         if(item){
18545             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18546         }
18547     },
18548
18549     onItemClick : function(item, index, e)
18550     {
18551         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18552             return false;
18553         }
18554         if (this.toggleSelect) {
18555             var m = this.isSelected(item) ? 'unselect' : 'select';
18556             //Roo.log(m);
18557             var _t = this;
18558             _t[m](item, true, false);
18559             return true;
18560         }
18561         if(this.multiSelect || this.singleSelect){
18562             if(this.multiSelect && e.shiftKey && this.lastSelection){
18563                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18564             }else{
18565                 this.select(item, this.multiSelect && e.ctrlKey);
18566                 this.lastSelection = item;
18567             }
18568             
18569             if(!this.tickable){
18570                 e.preventDefault();
18571             }
18572             
18573         }
18574         return true;
18575     },
18576
18577     /**
18578      * Get the number of selected nodes.
18579      * @return {Number}
18580      */
18581     getSelectionCount : function(){
18582         return this.selections.length;
18583     },
18584
18585     /**
18586      * Get the currently selected nodes.
18587      * @return {Array} An array of HTMLElements
18588      */
18589     getSelectedNodes : function(){
18590         return this.selections;
18591     },
18592
18593     /**
18594      * Get the indexes of the selected nodes.
18595      * @return {Array}
18596      */
18597     getSelectedIndexes : function(){
18598         var indexes = [], s = this.selections;
18599         for(var i = 0, len = s.length; i < len; i++){
18600             indexes.push(s[i].nodeIndex);
18601         }
18602         return indexes;
18603     },
18604
18605     /**
18606      * Clear all selections
18607      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18608      */
18609     clearSelections : function(suppressEvent){
18610         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18611             this.cmp.elements = this.selections;
18612             this.cmp.removeClass(this.selectedClass);
18613             this.selections = [];
18614             if(!suppressEvent){
18615                 this.fireEvent("selectionchange", this, this.selections);
18616             }
18617         }
18618     },
18619
18620     /**
18621      * Returns true if the passed node is selected
18622      * @param {HTMLElement/Number} node The node or node index
18623      * @return {Boolean}
18624      */
18625     isSelected : function(node){
18626         var s = this.selections;
18627         if(s.length < 1){
18628             return false;
18629         }
18630         node = this.getNode(node);
18631         return s.indexOf(node) !== -1;
18632     },
18633
18634     /**
18635      * Selects nodes.
18636      * @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
18637      * @param {Boolean} keepExisting (optional) true to keep existing selections
18638      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18639      */
18640     select : function(nodeInfo, keepExisting, suppressEvent){
18641         if(nodeInfo instanceof Array){
18642             if(!keepExisting){
18643                 this.clearSelections(true);
18644             }
18645             for(var i = 0, len = nodeInfo.length; i < len; i++){
18646                 this.select(nodeInfo[i], true, true);
18647             }
18648             return;
18649         } 
18650         var node = this.getNode(nodeInfo);
18651         if(!node || this.isSelected(node)){
18652             return; // already selected.
18653         }
18654         if(!keepExisting){
18655             this.clearSelections(true);
18656         }
18657         
18658         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18659             Roo.fly(node).addClass(this.selectedClass);
18660             this.selections.push(node);
18661             if(!suppressEvent){
18662                 this.fireEvent("selectionchange", this, this.selections);
18663             }
18664         }
18665         
18666         
18667     },
18668       /**
18669      * Unselects nodes.
18670      * @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
18671      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18672      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18673      */
18674     unselect : function(nodeInfo, keepExisting, suppressEvent)
18675     {
18676         if(nodeInfo instanceof Array){
18677             Roo.each(this.selections, function(s) {
18678                 this.unselect(s, nodeInfo);
18679             }, this);
18680             return;
18681         }
18682         var node = this.getNode(nodeInfo);
18683         if(!node || !this.isSelected(node)){
18684             //Roo.log("not selected");
18685             return; // not selected.
18686         }
18687         // fireevent???
18688         var ns = [];
18689         Roo.each(this.selections, function(s) {
18690             if (s == node ) {
18691                 Roo.fly(node).removeClass(this.selectedClass);
18692
18693                 return;
18694             }
18695             ns.push(s);
18696         },this);
18697         
18698         this.selections= ns;
18699         this.fireEvent("selectionchange", this, this.selections);
18700     },
18701
18702     /**
18703      * Gets a template node.
18704      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18705      * @return {HTMLElement} The node or null if it wasn't found
18706      */
18707     getNode : function(nodeInfo){
18708         if(typeof nodeInfo == "string"){
18709             return document.getElementById(nodeInfo);
18710         }else if(typeof nodeInfo == "number"){
18711             return this.nodes[nodeInfo];
18712         }
18713         return nodeInfo;
18714     },
18715
18716     /**
18717      * Gets a range template nodes.
18718      * @param {Number} startIndex
18719      * @param {Number} endIndex
18720      * @return {Array} An array of nodes
18721      */
18722     getNodes : function(start, end){
18723         var ns = this.nodes;
18724         start = start || 0;
18725         end = typeof end == "undefined" ? ns.length - 1 : end;
18726         var nodes = [];
18727         if(start <= end){
18728             for(var i = start; i <= end; i++){
18729                 nodes.push(ns[i]);
18730             }
18731         } else{
18732             for(var i = start; i >= end; i--){
18733                 nodes.push(ns[i]);
18734             }
18735         }
18736         return nodes;
18737     },
18738
18739     /**
18740      * Finds the index of the passed node
18741      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18742      * @return {Number} The index of the node or -1
18743      */
18744     indexOf : function(node){
18745         node = this.getNode(node);
18746         if(typeof node.nodeIndex == "number"){
18747             return node.nodeIndex;
18748         }
18749         var ns = this.nodes;
18750         for(var i = 0, len = ns.length; i < len; i++){
18751             if(ns[i] == node){
18752                 return i;
18753             }
18754         }
18755         return -1;
18756     }
18757 });
18758 /*
18759  * - LGPL
18760  *
18761  * based on jquery fullcalendar
18762  * 
18763  */
18764
18765 Roo.bootstrap = Roo.bootstrap || {};
18766 /**
18767  * @class Roo.bootstrap.Calendar
18768  * @extends Roo.bootstrap.Component
18769  * Bootstrap Calendar class
18770  * @cfg {Boolean} loadMask (true|false) default false
18771  * @cfg {Object} header generate the user specific header of the calendar, default false
18772
18773  * @constructor
18774  * Create a new Container
18775  * @param {Object} config The config object
18776  */
18777
18778
18779
18780 Roo.bootstrap.Calendar = function(config){
18781     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18782      this.addEvents({
18783         /**
18784              * @event select
18785              * Fires when a date is selected
18786              * @param {DatePicker} this
18787              * @param {Date} date The selected date
18788              */
18789         'select': true,
18790         /**
18791              * @event monthchange
18792              * Fires when the displayed month changes 
18793              * @param {DatePicker} this
18794              * @param {Date} date The selected month
18795              */
18796         'monthchange': true,
18797         /**
18798              * @event evententer
18799              * Fires when mouse over an event
18800              * @param {Calendar} this
18801              * @param {event} Event
18802              */
18803         'evententer': true,
18804         /**
18805              * @event eventleave
18806              * Fires when the mouse leaves an
18807              * @param {Calendar} this
18808              * @param {event}
18809              */
18810         'eventleave': true,
18811         /**
18812              * @event eventclick
18813              * Fires when the mouse click an
18814              * @param {Calendar} this
18815              * @param {event}
18816              */
18817         'eventclick': true
18818         
18819     });
18820
18821 };
18822
18823 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
18824     
18825      /**
18826      * @cfg {Number} startDay
18827      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18828      */
18829     startDay : 0,
18830     
18831     loadMask : false,
18832     
18833     header : false,
18834       
18835     getAutoCreate : function(){
18836         
18837         
18838         var fc_button = function(name, corner, style, content ) {
18839             return Roo.apply({},{
18840                 tag : 'span',
18841                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
18842                          (corner.length ?
18843                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18844                             ''
18845                         ),
18846                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18847                 unselectable: 'on'
18848             });
18849         };
18850         
18851         var header = {};
18852         
18853         if(!this.header){
18854             header = {
18855                 tag : 'table',
18856                 cls : 'fc-header',
18857                 style : 'width:100%',
18858                 cn : [
18859                     {
18860                         tag: 'tr',
18861                         cn : [
18862                             {
18863                                 tag : 'td',
18864                                 cls : 'fc-header-left',
18865                                 cn : [
18866                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
18867                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
18868                                     { tag: 'span', cls: 'fc-header-space' },
18869                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
18870
18871
18872                                 ]
18873                             },
18874
18875                             {
18876                                 tag : 'td',
18877                                 cls : 'fc-header-center',
18878                                 cn : [
18879                                     {
18880                                         tag: 'span',
18881                                         cls: 'fc-header-title',
18882                                         cn : {
18883                                             tag: 'H2',
18884                                             html : 'month / year'
18885                                         }
18886                                     }
18887
18888                                 ]
18889                             },
18890                             {
18891                                 tag : 'td',
18892                                 cls : 'fc-header-right',
18893                                 cn : [
18894                               /*      fc_button('month', 'left', '', 'month' ),
18895                                     fc_button('week', '', '', 'week' ),
18896                                     fc_button('day', 'right', '', 'day' )
18897                                 */    
18898
18899                                 ]
18900                             }
18901
18902                         ]
18903                     }
18904                 ]
18905             };
18906         }
18907         
18908         header = this.header;
18909         
18910        
18911         var cal_heads = function() {
18912             var ret = [];
18913             // fixme - handle this.
18914             
18915             for (var i =0; i < Date.dayNames.length; i++) {
18916                 var d = Date.dayNames[i];
18917                 ret.push({
18918                     tag: 'th',
18919                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18920                     html : d.substring(0,3)
18921                 });
18922                 
18923             }
18924             ret[0].cls += ' fc-first';
18925             ret[6].cls += ' fc-last';
18926             return ret;
18927         };
18928         var cal_cell = function(n) {
18929             return  {
18930                 tag: 'td',
18931                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18932                 cn : [
18933                     {
18934                         cn : [
18935                             {
18936                                 cls: 'fc-day-number',
18937                                 html: 'D'
18938                             },
18939                             {
18940                                 cls: 'fc-day-content',
18941                              
18942                                 cn : [
18943                                      {
18944                                         style: 'position: relative;' // height: 17px;
18945                                     }
18946                                 ]
18947                             }
18948                             
18949                             
18950                         ]
18951                     }
18952                 ]
18953                 
18954             }
18955         };
18956         var cal_rows = function() {
18957             
18958             var ret = [];
18959             for (var r = 0; r < 6; r++) {
18960                 var row= {
18961                     tag : 'tr',
18962                     cls : 'fc-week',
18963                     cn : []
18964                 };
18965                 
18966                 for (var i =0; i < Date.dayNames.length; i++) {
18967                     var d = Date.dayNames[i];
18968                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18969
18970                 }
18971                 row.cn[0].cls+=' fc-first';
18972                 row.cn[0].cn[0].style = 'min-height:90px';
18973                 row.cn[6].cls+=' fc-last';
18974                 ret.push(row);
18975                 
18976             }
18977             ret[0].cls += ' fc-first';
18978             ret[4].cls += ' fc-prev-last';
18979             ret[5].cls += ' fc-last';
18980             return ret;
18981             
18982         };
18983         
18984         var cal_table = {
18985             tag: 'table',
18986             cls: 'fc-border-separate',
18987             style : 'width:100%',
18988             cellspacing  : 0,
18989             cn : [
18990                 { 
18991                     tag: 'thead',
18992                     cn : [
18993                         { 
18994                             tag: 'tr',
18995                             cls : 'fc-first fc-last',
18996                             cn : cal_heads()
18997                         }
18998                     ]
18999                 },
19000                 { 
19001                     tag: 'tbody',
19002                     cn : cal_rows()
19003                 }
19004                   
19005             ]
19006         };
19007          
19008          var cfg = {
19009             cls : 'fc fc-ltr',
19010             cn : [
19011                 header,
19012                 {
19013                     cls : 'fc-content',
19014                     style : "position: relative;",
19015                     cn : [
19016                         {
19017                             cls : 'fc-view fc-view-month fc-grid',
19018                             style : 'position: relative',
19019                             unselectable : 'on',
19020                             cn : [
19021                                 {
19022                                     cls : 'fc-event-container',
19023                                     style : 'position:absolute;z-index:8;top:0;left:0;'
19024                                 },
19025                                 cal_table
19026                             ]
19027                         }
19028                     ]
19029     
19030                 }
19031            ] 
19032             
19033         };
19034         
19035          
19036         
19037         return cfg;
19038     },
19039     
19040     
19041     initEvents : function()
19042     {
19043         if(!this.store){
19044             throw "can not find store for calendar";
19045         }
19046         
19047         var mark = {
19048             tag: "div",
19049             cls:"x-dlg-mask",
19050             style: "text-align:center",
19051             cn: [
19052                 {
19053                     tag: "div",
19054                     style: "background-color:white;width:50%;margin:250 auto",
19055                     cn: [
19056                         {
19057                             tag: "img",
19058                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
19059                         },
19060                         {
19061                             tag: "span",
19062                             html: "Loading"
19063                         }
19064                         
19065                     ]
19066                 }
19067             ]
19068         };
19069         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19070         
19071         var size = this.el.select('.fc-content', true).first().getSize();
19072         this.maskEl.setSize(size.width, size.height);
19073         this.maskEl.enableDisplayMode("block");
19074         if(!this.loadMask){
19075             this.maskEl.hide();
19076         }
19077         
19078         this.store = Roo.factory(this.store, Roo.data);
19079         this.store.on('load', this.onLoad, this);
19080         this.store.on('beforeload', this.onBeforeLoad, this);
19081         
19082         this.resize();
19083         
19084         this.cells = this.el.select('.fc-day',true);
19085         //Roo.log(this.cells);
19086         this.textNodes = this.el.query('.fc-day-number');
19087         this.cells.addClassOnOver('fc-state-hover');
19088         
19089         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19090         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19091         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19092         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19093         
19094         this.on('monthchange', this.onMonthChange, this);
19095         
19096         this.update(new Date().clearTime());
19097     },
19098     
19099     resize : function() {
19100         var sz  = this.el.getSize();
19101         
19102         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19103         this.el.select('.fc-day-content div',true).setHeight(34);
19104     },
19105     
19106     
19107     // private
19108     showPrevMonth : function(e){
19109         this.update(this.activeDate.add("mo", -1));
19110     },
19111     showToday : function(e){
19112         this.update(new Date().clearTime());
19113     },
19114     // private
19115     showNextMonth : function(e){
19116         this.update(this.activeDate.add("mo", 1));
19117     },
19118
19119     // private
19120     showPrevYear : function(){
19121         this.update(this.activeDate.add("y", -1));
19122     },
19123
19124     // private
19125     showNextYear : function(){
19126         this.update(this.activeDate.add("y", 1));
19127     },
19128
19129     
19130    // private
19131     update : function(date)
19132     {
19133         var vd = this.activeDate;
19134         this.activeDate = date;
19135 //        if(vd && this.el){
19136 //            var t = date.getTime();
19137 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19138 //                Roo.log('using add remove');
19139 //                
19140 //                this.fireEvent('monthchange', this, date);
19141 //                
19142 //                this.cells.removeClass("fc-state-highlight");
19143 //                this.cells.each(function(c){
19144 //                   if(c.dateValue == t){
19145 //                       c.addClass("fc-state-highlight");
19146 //                       setTimeout(function(){
19147 //                            try{c.dom.firstChild.focus();}catch(e){}
19148 //                       }, 50);
19149 //                       return false;
19150 //                   }
19151 //                   return true;
19152 //                });
19153 //                return;
19154 //            }
19155 //        }
19156         
19157         var days = date.getDaysInMonth();
19158         
19159         var firstOfMonth = date.getFirstDateOfMonth();
19160         var startingPos = firstOfMonth.getDay()-this.startDay;
19161         
19162         if(startingPos < this.startDay){
19163             startingPos += 7;
19164         }
19165         
19166         var pm = date.add(Date.MONTH, -1);
19167         var prevStart = pm.getDaysInMonth()-startingPos;
19168 //        
19169         this.cells = this.el.select('.fc-day',true);
19170         this.textNodes = this.el.query('.fc-day-number');
19171         this.cells.addClassOnOver('fc-state-hover');
19172         
19173         var cells = this.cells.elements;
19174         var textEls = this.textNodes;
19175         
19176         Roo.each(cells, function(cell){
19177             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19178         });
19179         
19180         days += startingPos;
19181
19182         // convert everything to numbers so it's fast
19183         var day = 86400000;
19184         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19185         //Roo.log(d);
19186         //Roo.log(pm);
19187         //Roo.log(prevStart);
19188         
19189         var today = new Date().clearTime().getTime();
19190         var sel = date.clearTime().getTime();
19191         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19192         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19193         var ddMatch = this.disabledDatesRE;
19194         var ddText = this.disabledDatesText;
19195         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19196         var ddaysText = this.disabledDaysText;
19197         var format = this.format;
19198         
19199         var setCellClass = function(cal, cell){
19200             cell.row = 0;
19201             cell.events = [];
19202             cell.more = [];
19203             //Roo.log('set Cell Class');
19204             cell.title = "";
19205             var t = d.getTime();
19206             
19207             //Roo.log(d);
19208             
19209             cell.dateValue = t;
19210             if(t == today){
19211                 cell.className += " fc-today";
19212                 cell.className += " fc-state-highlight";
19213                 cell.title = cal.todayText;
19214             }
19215             if(t == sel){
19216                 // disable highlight in other month..
19217                 //cell.className += " fc-state-highlight";
19218                 
19219             }
19220             // disabling
19221             if(t < min) {
19222                 cell.className = " fc-state-disabled";
19223                 cell.title = cal.minText;
19224                 return;
19225             }
19226             if(t > max) {
19227                 cell.className = " fc-state-disabled";
19228                 cell.title = cal.maxText;
19229                 return;
19230             }
19231             if(ddays){
19232                 if(ddays.indexOf(d.getDay()) != -1){
19233                     cell.title = ddaysText;
19234                     cell.className = " fc-state-disabled";
19235                 }
19236             }
19237             if(ddMatch && format){
19238                 var fvalue = d.dateFormat(format);
19239                 if(ddMatch.test(fvalue)){
19240                     cell.title = ddText.replace("%0", fvalue);
19241                     cell.className = " fc-state-disabled";
19242                 }
19243             }
19244             
19245             if (!cell.initialClassName) {
19246                 cell.initialClassName = cell.dom.className;
19247             }
19248             
19249             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19250         };
19251
19252         var i = 0;
19253         
19254         for(; i < startingPos; i++) {
19255             textEls[i].innerHTML = (++prevStart);
19256             d.setDate(d.getDate()+1);
19257             
19258             cells[i].className = "fc-past fc-other-month";
19259             setCellClass(this, cells[i]);
19260         }
19261         
19262         var intDay = 0;
19263         
19264         for(; i < days; i++){
19265             intDay = i - startingPos + 1;
19266             textEls[i].innerHTML = (intDay);
19267             d.setDate(d.getDate()+1);
19268             
19269             cells[i].className = ''; // "x-date-active";
19270             setCellClass(this, cells[i]);
19271         }
19272         var extraDays = 0;
19273         
19274         for(; i < 42; i++) {
19275             textEls[i].innerHTML = (++extraDays);
19276             d.setDate(d.getDate()+1);
19277             
19278             cells[i].className = "fc-future fc-other-month";
19279             setCellClass(this, cells[i]);
19280         }
19281         
19282         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19283         
19284         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19285         
19286         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19287         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19288         
19289         if(totalRows != 6){
19290             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19291             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19292         }
19293         
19294         this.fireEvent('monthchange', this, date);
19295         
19296         
19297         /*
19298         if(!this.internalRender){
19299             var main = this.el.dom.firstChild;
19300             var w = main.offsetWidth;
19301             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19302             Roo.fly(main).setWidth(w);
19303             this.internalRender = true;
19304             // opera does not respect the auto grow header center column
19305             // then, after it gets a width opera refuses to recalculate
19306             // without a second pass
19307             if(Roo.isOpera && !this.secondPass){
19308                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19309                 this.secondPass = true;
19310                 this.update.defer(10, this, [date]);
19311             }
19312         }
19313         */
19314         
19315     },
19316     
19317     findCell : function(dt) {
19318         dt = dt.clearTime().getTime();
19319         var ret = false;
19320         this.cells.each(function(c){
19321             //Roo.log("check " +c.dateValue + '?=' + dt);
19322             if(c.dateValue == dt){
19323                 ret = c;
19324                 return false;
19325             }
19326             return true;
19327         });
19328         
19329         return ret;
19330     },
19331     
19332     findCells : function(ev) {
19333         var s = ev.start.clone().clearTime().getTime();
19334        // Roo.log(s);
19335         var e= ev.end.clone().clearTime().getTime();
19336        // Roo.log(e);
19337         var ret = [];
19338         this.cells.each(function(c){
19339              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19340             
19341             if(c.dateValue > e){
19342                 return ;
19343             }
19344             if(c.dateValue < s){
19345                 return ;
19346             }
19347             ret.push(c);
19348         });
19349         
19350         return ret;    
19351     },
19352     
19353 //    findBestRow: function(cells)
19354 //    {
19355 //        var ret = 0;
19356 //        
19357 //        for (var i =0 ; i < cells.length;i++) {
19358 //            ret  = Math.max(cells[i].rows || 0,ret);
19359 //        }
19360 //        return ret;
19361 //        
19362 //    },
19363     
19364     
19365     addItem : function(ev)
19366     {
19367         // look for vertical location slot in
19368         var cells = this.findCells(ev);
19369         
19370 //        ev.row = this.findBestRow(cells);
19371         
19372         // work out the location.
19373         
19374         var crow = false;
19375         var rows = [];
19376         for(var i =0; i < cells.length; i++) {
19377             
19378             cells[i].row = cells[0].row;
19379             
19380             if(i == 0){
19381                 cells[i].row = cells[i].row + 1;
19382             }
19383             
19384             if (!crow) {
19385                 crow = {
19386                     start : cells[i],
19387                     end :  cells[i]
19388                 };
19389                 continue;
19390             }
19391             if (crow.start.getY() == cells[i].getY()) {
19392                 // on same row.
19393                 crow.end = cells[i];
19394                 continue;
19395             }
19396             // different row.
19397             rows.push(crow);
19398             crow = {
19399                 start: cells[i],
19400                 end : cells[i]
19401             };
19402             
19403         }
19404         
19405         rows.push(crow);
19406         ev.els = [];
19407         ev.rows = rows;
19408         ev.cells = cells;
19409         
19410         cells[0].events.push(ev);
19411         
19412         this.calevents.push(ev);
19413     },
19414     
19415     clearEvents: function() {
19416         
19417         if(!this.calevents){
19418             return;
19419         }
19420         
19421         Roo.each(this.cells.elements, function(c){
19422             c.row = 0;
19423             c.events = [];
19424             c.more = [];
19425         });
19426         
19427         Roo.each(this.calevents, function(e) {
19428             Roo.each(e.els, function(el) {
19429                 el.un('mouseenter' ,this.onEventEnter, this);
19430                 el.un('mouseleave' ,this.onEventLeave, this);
19431                 el.remove();
19432             },this);
19433         },this);
19434         
19435         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19436             e.remove();
19437         });
19438         
19439     },
19440     
19441     renderEvents: function()
19442     {   
19443         var _this = this;
19444         
19445         this.cells.each(function(c) {
19446             
19447             if(c.row < 5){
19448                 return;
19449             }
19450             
19451             var ev = c.events;
19452             
19453             var r = 4;
19454             if(c.row != c.events.length){
19455                 r = 4 - (4 - (c.row - c.events.length));
19456             }
19457             
19458             c.events = ev.slice(0, r);
19459             c.more = ev.slice(r);
19460             
19461             if(c.more.length && c.more.length == 1){
19462                 c.events.push(c.more.pop());
19463             }
19464             
19465             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19466             
19467         });
19468             
19469         this.cells.each(function(c) {
19470             
19471             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19472             
19473             
19474             for (var e = 0; e < c.events.length; e++){
19475                 var ev = c.events[e];
19476                 var rows = ev.rows;
19477                 
19478                 for(var i = 0; i < rows.length; i++) {
19479                 
19480                     // how many rows should it span..
19481
19482                     var  cfg = {
19483                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19484                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19485
19486                         unselectable : "on",
19487                         cn : [
19488                             {
19489                                 cls: 'fc-event-inner',
19490                                 cn : [
19491     //                                {
19492     //                                  tag:'span',
19493     //                                  cls: 'fc-event-time',
19494     //                                  html : cells.length > 1 ? '' : ev.time
19495     //                                },
19496                                     {
19497                                       tag:'span',
19498                                       cls: 'fc-event-title',
19499                                       html : String.format('{0}', ev.title)
19500                                     }
19501
19502
19503                                 ]
19504                             },
19505                             {
19506                                 cls: 'ui-resizable-handle ui-resizable-e',
19507                                 html : '&nbsp;&nbsp;&nbsp'
19508                             }
19509
19510                         ]
19511                     };
19512
19513                     if (i == 0) {
19514                         cfg.cls += ' fc-event-start';
19515                     }
19516                     if ((i+1) == rows.length) {
19517                         cfg.cls += ' fc-event-end';
19518                     }
19519
19520                     var ctr = _this.el.select('.fc-event-container',true).first();
19521                     var cg = ctr.createChild(cfg);
19522
19523                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19524                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19525
19526                     var r = (c.more.length) ? 1 : 0;
19527                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19528                     cg.setWidth(ebox.right - sbox.x -2);
19529
19530                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19531                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19532                     cg.on('click', _this.onEventClick, _this, ev);
19533
19534                     ev.els.push(cg);
19535                     
19536                 }
19537                 
19538             }
19539             
19540             
19541             if(c.more.length){
19542                 var  cfg = {
19543                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19544                     style : 'position: absolute',
19545                     unselectable : "on",
19546                     cn : [
19547                         {
19548                             cls: 'fc-event-inner',
19549                             cn : [
19550                                 {
19551                                   tag:'span',
19552                                   cls: 'fc-event-title',
19553                                   html : 'More'
19554                                 }
19555
19556
19557                             ]
19558                         },
19559                         {
19560                             cls: 'ui-resizable-handle ui-resizable-e',
19561                             html : '&nbsp;&nbsp;&nbsp'
19562                         }
19563
19564                     ]
19565                 };
19566
19567                 var ctr = _this.el.select('.fc-event-container',true).first();
19568                 var cg = ctr.createChild(cfg);
19569
19570                 var sbox = c.select('.fc-day-content',true).first().getBox();
19571                 var ebox = c.select('.fc-day-content',true).first().getBox();
19572                 //Roo.log(cg);
19573                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19574                 cg.setWidth(ebox.right - sbox.x -2);
19575
19576                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19577                 
19578             }
19579             
19580         });
19581         
19582         
19583         
19584     },
19585     
19586     onEventEnter: function (e, el,event,d) {
19587         this.fireEvent('evententer', this, el, event);
19588     },
19589     
19590     onEventLeave: function (e, el,event,d) {
19591         this.fireEvent('eventleave', this, el, event);
19592     },
19593     
19594     onEventClick: function (e, el,event,d) {
19595         this.fireEvent('eventclick', this, el, event);
19596     },
19597     
19598     onMonthChange: function () {
19599         this.store.load();
19600     },
19601     
19602     onMoreEventClick: function(e, el, more)
19603     {
19604         var _this = this;
19605         
19606         this.calpopover.placement = 'right';
19607         this.calpopover.setTitle('More');
19608         
19609         this.calpopover.setContent('');
19610         
19611         var ctr = this.calpopover.el.select('.popover-content', true).first();
19612         
19613         Roo.each(more, function(m){
19614             var cfg = {
19615                 cls : 'fc-event-hori fc-event-draggable',
19616                 html : m.title
19617             };
19618             var cg = ctr.createChild(cfg);
19619             
19620             cg.on('click', _this.onEventClick, _this, m);
19621         });
19622         
19623         this.calpopover.show(el);
19624         
19625         
19626     },
19627     
19628     onLoad: function () 
19629     {   
19630         this.calevents = [];
19631         var cal = this;
19632         
19633         if(this.store.getCount() > 0){
19634             this.store.data.each(function(d){
19635                cal.addItem({
19636                     id : d.data.id,
19637                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19638                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19639                     time : d.data.start_time,
19640                     title : d.data.title,
19641                     description : d.data.description,
19642                     venue : d.data.venue
19643                 });
19644             });
19645         }
19646         
19647         this.renderEvents();
19648         
19649         if(this.calevents.length && this.loadMask){
19650             this.maskEl.hide();
19651         }
19652     },
19653     
19654     onBeforeLoad: function()
19655     {
19656         this.clearEvents();
19657         if(this.loadMask){
19658             this.maskEl.show();
19659         }
19660     }
19661 });
19662
19663  
19664  /*
19665  * - LGPL
19666  *
19667  * element
19668  * 
19669  */
19670
19671 /**
19672  * @class Roo.bootstrap.Popover
19673  * @extends Roo.bootstrap.Component
19674  * Bootstrap Popover class
19675  * @cfg {String} html contents of the popover   (or false to use children..)
19676  * @cfg {String} title of popover (or false to hide)
19677  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19678  * @cfg {String} trigger click || hover (or false to trigger manually)
19679  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19680  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19681  *      - if false and it has a 'parent' then it will be automatically added to that element
19682  *      - if string - Roo.get  will be called 
19683  * @cfg {Number} delay - delay before showing
19684  
19685  * @constructor
19686  * Create a new Popover
19687  * @param {Object} config The config object
19688  */
19689
19690 Roo.bootstrap.Popover = function(config){
19691     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19692     
19693     this.addEvents({
19694         // raw events
19695          /**
19696          * @event show
19697          * After the popover show
19698          * 
19699          * @param {Roo.bootstrap.Popover} this
19700          */
19701         "show" : true,
19702         /**
19703          * @event hide
19704          * After the popover hide
19705          * 
19706          * @param {Roo.bootstrap.Popover} this
19707          */
19708         "hide" : true
19709     });
19710 };
19711
19712 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19713     
19714     title: false,
19715     html: false,
19716     
19717     placement : 'right',
19718     trigger : 'hover', // hover
19719     modal : false,
19720     delay : 0,
19721     
19722     over: false,
19723     
19724     can_build_overlaid : false,
19725     
19726     maskEl : false, // the mask element
19727     headerEl : false,
19728     contentEl : false,
19729     alignEl : false, // when show is called with an element - this get's stored.
19730     
19731     getChildContainer : function()
19732     {
19733         return this.contentEl;
19734         
19735     },
19736     getPopoverHeader : function()
19737     {
19738         this.title = true; // flag not to hide it..
19739         this.headerEl.addClass('p-0');
19740         return this.headerEl
19741     },
19742     
19743     
19744     getAutoCreate : function(){
19745          
19746         var cfg = {
19747            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19748            style: 'display:block',
19749            cn : [
19750                 {
19751                     cls : 'arrow'
19752                 },
19753                 {
19754                     cls : 'popover-inner ',
19755                     cn : [
19756                         {
19757                             tag: 'h3',
19758                             cls: 'popover-title popover-header',
19759                             html : this.title === false ? '' : this.title
19760                         },
19761                         {
19762                             cls : 'popover-content popover-body '  + (this.cls || ''),
19763                             html : this.html || ''
19764                         }
19765                     ]
19766                     
19767                 }
19768            ]
19769         };
19770         
19771         return cfg;
19772     },
19773     /**
19774      * @param {string} the title
19775      */
19776     setTitle: function(str)
19777     {
19778         this.title = str;
19779         if (this.el) {
19780             this.headerEl.dom.innerHTML = str;
19781         }
19782         
19783     },
19784     /**
19785      * @param {string} the body content
19786      */
19787     setContent: function(str)
19788     {
19789         this.html = str;
19790         if (this.contentEl) {
19791             this.contentEl.dom.innerHTML = str;
19792         }
19793         
19794     },
19795     // as it get's added to the bottom of the page.
19796     onRender : function(ct, position)
19797     {
19798         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19799         
19800         
19801         
19802         if(!this.el){
19803             var cfg = Roo.apply({},  this.getAutoCreate());
19804             cfg.id = Roo.id();
19805             
19806             if (this.cls) {
19807                 cfg.cls += ' ' + this.cls;
19808             }
19809             if (this.style) {
19810                 cfg.style = this.style;
19811             }
19812             //Roo.log("adding to ");
19813             this.el = Roo.get(document.body).createChild(cfg, position);
19814 //            Roo.log(this.el);
19815         }
19816         
19817         this.contentEl = this.el.select('.popover-content',true).first();
19818         this.headerEl =  this.el.select('.popover-title',true).first();
19819         
19820         var nitems = [];
19821         if(typeof(this.items) != 'undefined'){
19822             var items = this.items;
19823             delete this.items;
19824
19825             for(var i =0;i < items.length;i++) {
19826                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19827             }
19828         }
19829
19830         this.items = nitems;
19831         
19832         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19833         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19834         
19835         
19836         
19837         this.initEvents();
19838     },
19839     
19840     resizeMask : function()
19841     {
19842         this.maskEl.setSize(
19843             Roo.lib.Dom.getViewWidth(true),
19844             Roo.lib.Dom.getViewHeight(true)
19845         );
19846     },
19847     
19848     initEvents : function()
19849     {
19850         
19851         if (!this.modal) { 
19852             Roo.bootstrap.Popover.register(this);
19853         }
19854          
19855         this.arrowEl = this.el.select('.arrow',true).first();
19856         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19857         this.el.enableDisplayMode('block');
19858         this.el.hide();
19859  
19860         
19861         if (this.over === false && !this.parent()) {
19862             return; 
19863         }
19864         if (this.triggers === false) {
19865             return;
19866         }
19867          
19868         // support parent
19869         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19870         var triggers = this.trigger ? this.trigger.split(' ') : [];
19871         Roo.each(triggers, function(trigger) {
19872         
19873             if (trigger == 'click') {
19874                 on_el.on('click', this.toggle, this);
19875             } else if (trigger != 'manual') {
19876                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19877                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19878       
19879                 on_el.on(eventIn  ,this.enter, this);
19880                 on_el.on(eventOut, this.leave, this);
19881             }
19882         }, this);
19883     },
19884     
19885     
19886     // private
19887     timeout : null,
19888     hoverState : null,
19889     
19890     toggle : function () {
19891         this.hoverState == 'in' ? this.leave() : this.enter();
19892     },
19893     
19894     enter : function () {
19895         
19896         clearTimeout(this.timeout);
19897     
19898         this.hoverState = 'in';
19899     
19900         if (!this.delay || !this.delay.show) {
19901             this.show();
19902             return;
19903         }
19904         var _t = this;
19905         this.timeout = setTimeout(function () {
19906             if (_t.hoverState == 'in') {
19907                 _t.show();
19908             }
19909         }, this.delay.show)
19910     },
19911     
19912     leave : function() {
19913         clearTimeout(this.timeout);
19914     
19915         this.hoverState = 'out';
19916     
19917         if (!this.delay || !this.delay.hide) {
19918             this.hide();
19919             return;
19920         }
19921         var _t = this;
19922         this.timeout = setTimeout(function () {
19923             if (_t.hoverState == 'out') {
19924                 _t.hide();
19925             }
19926         }, this.delay.hide)
19927     },
19928     /**
19929      * Show the popover
19930      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
19931      * @param {string} (left|right|top|bottom) position
19932      */
19933     show : function (on_el, placement)
19934     {
19935         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
19936         on_el = on_el || false; // default to false
19937          
19938         if (!on_el) {
19939             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19940                 on_el = this.parent().el;
19941             } else if (this.over) {
19942                 Roo.get(this.over);
19943             }
19944             
19945         }
19946         
19947         this.alignEl = Roo.get( on_el );
19948
19949         if (!this.el) {
19950             this.render(document.body);
19951         }
19952         
19953         
19954          
19955         
19956         if (this.title === false) {
19957             this.headerEl.hide();
19958         }
19959         
19960        
19961         this.el.show();
19962         this.el.dom.style.display = 'block';
19963          
19964  
19965         if (this.alignEl) {
19966             this.updatePosition(this.placement, true);
19967              
19968         } else {
19969             // this is usually just done by the builder = to show the popoup in the middle of the scren.
19970             var es = this.el.getSize();
19971             var x = Roo.lib.Dom.getViewWidth()/2;
19972             var y = Roo.lib.Dom.getViewHeight()/2;
19973             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
19974             
19975         }
19976
19977         
19978         //var arrow = this.el.select('.arrow',true).first();
19979         //arrow.set(align[2], 
19980         
19981         this.el.addClass('in');
19982         
19983          
19984         
19985         this.hoverState = 'in';
19986         
19987         if (this.modal) {
19988             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19989             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19990             this.maskEl.dom.style.display = 'block';
19991             this.maskEl.addClass('show');
19992         }
19993         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19994  
19995         this.fireEvent('show', this);
19996         
19997     },
19998     /**
19999      * fire this manually after loading a grid in the table for example
20000      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20001      * @param {Boolean} try and move it if we cant get right position.
20002      */
20003     updatePosition : function(placement, try_move)
20004     {
20005         // allow for calling with no parameters
20006         placement = placement   ? placement :  this.placement;
20007         try_move = typeof(try_move) == 'undefined' ? true : try_move;
20008         
20009         this.el.removeClass([
20010             'fade','top','bottom', 'left', 'right','in',
20011             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20012         ]);
20013         this.el.addClass(placement + ' bs-popover-' + placement);
20014         
20015         if (!this.alignEl ) {
20016             return false;
20017         }
20018         
20019         switch (placement) {
20020             case 'right':
20021                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20022                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20023                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20024                     //normal display... or moved up/down.
20025                     this.el.setXY(offset);
20026                     var xy = this.alignEl.getAnchorXY('tr', false);
20027                     xy[0]+=2;xy[1]+=5;
20028                     this.arrowEl.setXY(xy);
20029                     return true;
20030                 }
20031                 // continue through...
20032                 return this.updatePosition('left', false);
20033                 
20034             
20035             case 'left':
20036                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20037                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20038                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20039                     //normal display... or moved up/down.
20040                     this.el.setXY(offset);
20041                     var xy = this.alignEl.getAnchorXY('tl', false);
20042                     xy[0]-=10;xy[1]+=5; // << fix me
20043                     this.arrowEl.setXY(xy);
20044                     return true;
20045                 }
20046                 // call self...
20047                 return this.updatePosition('right', false);
20048             
20049             case 'top':
20050                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20051                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20052                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20053                     //normal display... or moved up/down.
20054                     this.el.setXY(offset);
20055                     var xy = this.alignEl.getAnchorXY('t', false);
20056                     xy[1]-=10; // << fix me
20057                     this.arrowEl.setXY(xy);
20058                     return true;
20059                 }
20060                 // fall through
20061                return this.updatePosition('bottom', false);
20062             
20063             case 'bottom':
20064                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20065                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20066                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20067                     //normal display... or moved up/down.
20068                     this.el.setXY(offset);
20069                     var xy = this.alignEl.getAnchorXY('b', false);
20070                      xy[1]+=2; // << fix me
20071                     this.arrowEl.setXY(xy);
20072                     return true;
20073                 }
20074                 // fall through
20075                 return this.updatePosition('top', false);
20076                 
20077             
20078         }
20079         
20080         
20081         return false;
20082     },
20083     
20084     hide : function()
20085     {
20086         this.el.setXY([0,0]);
20087         this.el.removeClass('in');
20088         this.el.hide();
20089         this.hoverState = null;
20090         this.maskEl.hide(); // always..
20091         this.fireEvent('hide', this);
20092     }
20093     
20094 });
20095
20096
20097 Roo.apply(Roo.bootstrap.Popover, {
20098
20099     alignment : {
20100         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20101         'right' : ['l-br', [10,0], 'right bs-popover-right'],
20102         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20103         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20104     },
20105     
20106     zIndex : 20001,
20107
20108     clickHander : false,
20109     
20110
20111     onMouseDown : function(e)
20112     {
20113         if (!e.getTarget(".roo-popover")) {
20114             this.hideAll();
20115         }
20116          
20117     },
20118     
20119     popups : [],
20120     
20121     register : function(popup)
20122     {
20123         if (!Roo.bootstrap.Popover.clickHandler) {
20124             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20125         }
20126         // hide other popups.
20127         this.hideAll();
20128         this.popups.push(popup);
20129     },
20130     hideAll : function()
20131     {
20132         this.popups.forEach(function(p) {
20133             p.hide();
20134         });
20135     }
20136
20137 });/*
20138  * - LGPL
20139  *
20140  * Card header - holder for the card header elements.
20141  * 
20142  */
20143
20144 /**
20145  * @class Roo.bootstrap.PopoverNav
20146  * @extends Roo.bootstrap.NavGroup
20147  * Bootstrap Popover header navigation class
20148  * @constructor
20149  * Create a new Popover Header Navigation 
20150  * @param {Object} config The config object
20151  */
20152
20153 Roo.bootstrap.PopoverNav = function(config){
20154     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20155 };
20156
20157 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
20158     
20159     
20160     container_method : 'getPopoverHeader' 
20161     
20162      
20163     
20164     
20165    
20166 });
20167
20168  
20169
20170  /*
20171  * - LGPL
20172  *
20173  * Progress
20174  * 
20175  */
20176
20177 /**
20178  * @class Roo.bootstrap.Progress
20179  * @extends Roo.bootstrap.Component
20180  * Bootstrap Progress class
20181  * @cfg {Boolean} striped striped of the progress bar
20182  * @cfg {Boolean} active animated of the progress bar
20183  * 
20184  * 
20185  * @constructor
20186  * Create a new Progress
20187  * @param {Object} config The config object
20188  */
20189
20190 Roo.bootstrap.Progress = function(config){
20191     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20192 };
20193
20194 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20195     
20196     striped : false,
20197     active: false,
20198     
20199     getAutoCreate : function(){
20200         var cfg = {
20201             tag: 'div',
20202             cls: 'progress'
20203         };
20204         
20205         
20206         if(this.striped){
20207             cfg.cls += ' progress-striped';
20208         }
20209       
20210         if(this.active){
20211             cfg.cls += ' active';
20212         }
20213         
20214         
20215         return cfg;
20216     }
20217    
20218 });
20219
20220  
20221
20222  /*
20223  * - LGPL
20224  *
20225  * ProgressBar
20226  * 
20227  */
20228
20229 /**
20230  * @class Roo.bootstrap.ProgressBar
20231  * @extends Roo.bootstrap.Component
20232  * Bootstrap ProgressBar class
20233  * @cfg {Number} aria_valuenow aria-value now
20234  * @cfg {Number} aria_valuemin aria-value min
20235  * @cfg {Number} aria_valuemax aria-value max
20236  * @cfg {String} label label for the progress bar
20237  * @cfg {String} panel (success | info | warning | danger )
20238  * @cfg {String} role role of the progress bar
20239  * @cfg {String} sr_only text
20240  * 
20241  * 
20242  * @constructor
20243  * Create a new ProgressBar
20244  * @param {Object} config The config object
20245  */
20246
20247 Roo.bootstrap.ProgressBar = function(config){
20248     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20249 };
20250
20251 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20252     
20253     aria_valuenow : 0,
20254     aria_valuemin : 0,
20255     aria_valuemax : 100,
20256     label : false,
20257     panel : false,
20258     role : false,
20259     sr_only: false,
20260     
20261     getAutoCreate : function()
20262     {
20263         
20264         var cfg = {
20265             tag: 'div',
20266             cls: 'progress-bar',
20267             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20268         };
20269         
20270         if(this.sr_only){
20271             cfg.cn = {
20272                 tag: 'span',
20273                 cls: 'sr-only',
20274                 html: this.sr_only
20275             }
20276         }
20277         
20278         if(this.role){
20279             cfg.role = this.role;
20280         }
20281         
20282         if(this.aria_valuenow){
20283             cfg['aria-valuenow'] = this.aria_valuenow;
20284         }
20285         
20286         if(this.aria_valuemin){
20287             cfg['aria-valuemin'] = this.aria_valuemin;
20288         }
20289         
20290         if(this.aria_valuemax){
20291             cfg['aria-valuemax'] = this.aria_valuemax;
20292         }
20293         
20294         if(this.label && !this.sr_only){
20295             cfg.html = this.label;
20296         }
20297         
20298         if(this.panel){
20299             cfg.cls += ' progress-bar-' + this.panel;
20300         }
20301         
20302         return cfg;
20303     },
20304     
20305     update : function(aria_valuenow)
20306     {
20307         this.aria_valuenow = aria_valuenow;
20308         
20309         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20310     }
20311    
20312 });
20313
20314  
20315
20316  /*
20317  * - LGPL
20318  *
20319  * column
20320  * 
20321  */
20322
20323 /**
20324  * @class Roo.bootstrap.TabGroup
20325  * @extends Roo.bootstrap.Column
20326  * Bootstrap Column class
20327  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20328  * @cfg {Boolean} carousel true to make the group behave like a carousel
20329  * @cfg {Boolean} bullets show bullets for the panels
20330  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20331  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20332  * @cfg {Boolean} showarrow (true|false) show arrow default true
20333  * 
20334  * @constructor
20335  * Create a new TabGroup
20336  * @param {Object} config The config object
20337  */
20338
20339 Roo.bootstrap.TabGroup = function(config){
20340     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20341     if (!this.navId) {
20342         this.navId = Roo.id();
20343     }
20344     this.tabs = [];
20345     Roo.bootstrap.TabGroup.register(this);
20346     
20347 };
20348
20349 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20350     
20351     carousel : false,
20352     transition : false,
20353     bullets : 0,
20354     timer : 0,
20355     autoslide : false,
20356     slideFn : false,
20357     slideOnTouch : false,
20358     showarrow : true,
20359     
20360     getAutoCreate : function()
20361     {
20362         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20363         
20364         cfg.cls += ' tab-content';
20365         
20366         if (this.carousel) {
20367             cfg.cls += ' carousel slide';
20368             
20369             cfg.cn = [{
20370                cls : 'carousel-inner',
20371                cn : []
20372             }];
20373         
20374             if(this.bullets  && !Roo.isTouch){
20375                 
20376                 var bullets = {
20377                     cls : 'carousel-bullets',
20378                     cn : []
20379                 };
20380                
20381                 if(this.bullets_cls){
20382                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20383                 }
20384                 
20385                 bullets.cn.push({
20386                     cls : 'clear'
20387                 });
20388                 
20389                 cfg.cn[0].cn.push(bullets);
20390             }
20391             
20392             if(this.showarrow){
20393                 cfg.cn[0].cn.push({
20394                     tag : 'div',
20395                     class : 'carousel-arrow',
20396                     cn : [
20397                         {
20398                             tag : 'div',
20399                             class : 'carousel-prev',
20400                             cn : [
20401                                 {
20402                                     tag : 'i',
20403                                     class : 'fa fa-chevron-left'
20404                                 }
20405                             ]
20406                         },
20407                         {
20408                             tag : 'div',
20409                             class : 'carousel-next',
20410                             cn : [
20411                                 {
20412                                     tag : 'i',
20413                                     class : 'fa fa-chevron-right'
20414                                 }
20415                             ]
20416                         }
20417                     ]
20418                 });
20419             }
20420             
20421         }
20422         
20423         return cfg;
20424     },
20425     
20426     initEvents:  function()
20427     {
20428 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20429 //            this.el.on("touchstart", this.onTouchStart, this);
20430 //        }
20431         
20432         if(this.autoslide){
20433             var _this = this;
20434             
20435             this.slideFn = window.setInterval(function() {
20436                 _this.showPanelNext();
20437             }, this.timer);
20438         }
20439         
20440         if(this.showarrow){
20441             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20442             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20443         }
20444         
20445         
20446     },
20447     
20448 //    onTouchStart : function(e, el, o)
20449 //    {
20450 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20451 //            return;
20452 //        }
20453 //        
20454 //        this.showPanelNext();
20455 //    },
20456     
20457     
20458     getChildContainer : function()
20459     {
20460         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20461     },
20462     
20463     /**
20464     * register a Navigation item
20465     * @param {Roo.bootstrap.NavItem} the navitem to add
20466     */
20467     register : function(item)
20468     {
20469         this.tabs.push( item);
20470         item.navId = this.navId; // not really needed..
20471         this.addBullet();
20472     
20473     },
20474     
20475     getActivePanel : function()
20476     {
20477         var r = false;
20478         Roo.each(this.tabs, function(t) {
20479             if (t.active) {
20480                 r = t;
20481                 return false;
20482             }
20483             return null;
20484         });
20485         return r;
20486         
20487     },
20488     getPanelByName : function(n)
20489     {
20490         var r = false;
20491         Roo.each(this.tabs, function(t) {
20492             if (t.tabId == n) {
20493                 r = t;
20494                 return false;
20495             }
20496             return null;
20497         });
20498         return r;
20499     },
20500     indexOfPanel : function(p)
20501     {
20502         var r = false;
20503         Roo.each(this.tabs, function(t,i) {
20504             if (t.tabId == p.tabId) {
20505                 r = i;
20506                 return false;
20507             }
20508             return null;
20509         });
20510         return r;
20511     },
20512     /**
20513      * show a specific panel
20514      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20515      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20516      */
20517     showPanel : function (pan)
20518     {
20519         if(this.transition || typeof(pan) == 'undefined'){
20520             Roo.log("waiting for the transitionend");
20521             return false;
20522         }
20523         
20524         if (typeof(pan) == 'number') {
20525             pan = this.tabs[pan];
20526         }
20527         
20528         if (typeof(pan) == 'string') {
20529             pan = this.getPanelByName(pan);
20530         }
20531         
20532         var cur = this.getActivePanel();
20533         
20534         if(!pan || !cur){
20535             Roo.log('pan or acitve pan is undefined');
20536             return false;
20537         }
20538         
20539         if (pan.tabId == this.getActivePanel().tabId) {
20540             return true;
20541         }
20542         
20543         if (false === cur.fireEvent('beforedeactivate')) {
20544             return false;
20545         }
20546         
20547         if(this.bullets > 0 && !Roo.isTouch){
20548             this.setActiveBullet(this.indexOfPanel(pan));
20549         }
20550         
20551         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20552             
20553             //class="carousel-item carousel-item-next carousel-item-left"
20554             
20555             this.transition = true;
20556             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20557             var lr = dir == 'next' ? 'left' : 'right';
20558             pan.el.addClass(dir); // or prev
20559             pan.el.addClass('carousel-item-' + dir); // or prev
20560             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20561             cur.el.addClass(lr); // or right
20562             pan.el.addClass(lr);
20563             cur.el.addClass('carousel-item-' +lr); // or right
20564             pan.el.addClass('carousel-item-' +lr);
20565             
20566             
20567             var _this = this;
20568             cur.el.on('transitionend', function() {
20569                 Roo.log("trans end?");
20570                 
20571                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20572                 pan.setActive(true);
20573                 
20574                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20575                 cur.setActive(false);
20576                 
20577                 _this.transition = false;
20578                 
20579             }, this, { single:  true } );
20580             
20581             return true;
20582         }
20583         
20584         cur.setActive(false);
20585         pan.setActive(true);
20586         
20587         return true;
20588         
20589     },
20590     showPanelNext : function()
20591     {
20592         var i = this.indexOfPanel(this.getActivePanel());
20593         
20594         if (i >= this.tabs.length - 1 && !this.autoslide) {
20595             return;
20596         }
20597         
20598         if (i >= this.tabs.length - 1 && this.autoslide) {
20599             i = -1;
20600         }
20601         
20602         this.showPanel(this.tabs[i+1]);
20603     },
20604     
20605     showPanelPrev : function()
20606     {
20607         var i = this.indexOfPanel(this.getActivePanel());
20608         
20609         if (i  < 1 && !this.autoslide) {
20610             return;
20611         }
20612         
20613         if (i < 1 && this.autoslide) {
20614             i = this.tabs.length;
20615         }
20616         
20617         this.showPanel(this.tabs[i-1]);
20618     },
20619     
20620     
20621     addBullet: function()
20622     {
20623         if(!this.bullets || Roo.isTouch){
20624             return;
20625         }
20626         var ctr = this.el.select('.carousel-bullets',true).first();
20627         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20628         var bullet = ctr.createChild({
20629             cls : 'bullet bullet-' + i
20630         },ctr.dom.lastChild);
20631         
20632         
20633         var _this = this;
20634         
20635         bullet.on('click', (function(e, el, o, ii, t){
20636
20637             e.preventDefault();
20638
20639             this.showPanel(ii);
20640
20641             if(this.autoslide && this.slideFn){
20642                 clearInterval(this.slideFn);
20643                 this.slideFn = window.setInterval(function() {
20644                     _this.showPanelNext();
20645                 }, this.timer);
20646             }
20647
20648         }).createDelegate(this, [i, bullet], true));
20649                 
20650         
20651     },
20652      
20653     setActiveBullet : function(i)
20654     {
20655         if(Roo.isTouch){
20656             return;
20657         }
20658         
20659         Roo.each(this.el.select('.bullet', true).elements, function(el){
20660             el.removeClass('selected');
20661         });
20662
20663         var bullet = this.el.select('.bullet-' + i, true).first();
20664         
20665         if(!bullet){
20666             return;
20667         }
20668         
20669         bullet.addClass('selected');
20670     }
20671     
20672     
20673   
20674 });
20675
20676  
20677
20678  
20679  
20680 Roo.apply(Roo.bootstrap.TabGroup, {
20681     
20682     groups: {},
20683      /**
20684     * register a Navigation Group
20685     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20686     */
20687     register : function(navgrp)
20688     {
20689         this.groups[navgrp.navId] = navgrp;
20690         
20691     },
20692     /**
20693     * fetch a Navigation Group based on the navigation ID
20694     * if one does not exist , it will get created.
20695     * @param {string} the navgroup to add
20696     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20697     */
20698     get: function(navId) {
20699         if (typeof(this.groups[navId]) == 'undefined') {
20700             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20701         }
20702         return this.groups[navId] ;
20703     }
20704     
20705     
20706     
20707 });
20708
20709  /*
20710  * - LGPL
20711  *
20712  * TabPanel
20713  * 
20714  */
20715
20716 /**
20717  * @class Roo.bootstrap.TabPanel
20718  * @extends Roo.bootstrap.Component
20719  * Bootstrap TabPanel class
20720  * @cfg {Boolean} active panel active
20721  * @cfg {String} html panel content
20722  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20723  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20724  * @cfg {String} href click to link..
20725  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20726  * 
20727  * 
20728  * @constructor
20729  * Create a new TabPanel
20730  * @param {Object} config The config object
20731  */
20732
20733 Roo.bootstrap.TabPanel = function(config){
20734     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20735     this.addEvents({
20736         /**
20737              * @event changed
20738              * Fires when the active status changes
20739              * @param {Roo.bootstrap.TabPanel} this
20740              * @param {Boolean} state the new state
20741             
20742          */
20743         'changed': true,
20744         /**
20745              * @event beforedeactivate
20746              * Fires before a tab is de-activated - can be used to do validation on a form.
20747              * @param {Roo.bootstrap.TabPanel} this
20748              * @return {Boolean} false if there is an error
20749             
20750          */
20751         'beforedeactivate': true
20752      });
20753     
20754     this.tabId = this.tabId || Roo.id();
20755   
20756 };
20757
20758 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20759     
20760     active: false,
20761     html: false,
20762     tabId: false,
20763     navId : false,
20764     href : '',
20765     touchSlide : false,
20766     getAutoCreate : function(){
20767         
20768         
20769         var cfg = {
20770             tag: 'div',
20771             // item is needed for carousel - not sure if it has any effect otherwise
20772             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20773             html: this.html || ''
20774         };
20775         
20776         if(this.active){
20777             cfg.cls += ' active';
20778         }
20779         
20780         if(this.tabId){
20781             cfg.tabId = this.tabId;
20782         }
20783         
20784         
20785         
20786         return cfg;
20787     },
20788     
20789     initEvents:  function()
20790     {
20791         var p = this.parent();
20792         
20793         this.navId = this.navId || p.navId;
20794         
20795         if (typeof(this.navId) != 'undefined') {
20796             // not really needed.. but just in case.. parent should be a NavGroup.
20797             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20798             
20799             tg.register(this);
20800             
20801             var i = tg.tabs.length - 1;
20802             
20803             if(this.active && tg.bullets > 0 && i < tg.bullets){
20804                 tg.setActiveBullet(i);
20805             }
20806         }
20807         
20808         this.el.on('click', this.onClick, this);
20809         
20810         if(Roo.isTouch && this.touchSlide){
20811             this.el.on("touchstart", this.onTouchStart, this);
20812             this.el.on("touchmove", this.onTouchMove, this);
20813             this.el.on("touchend", this.onTouchEnd, this);
20814         }
20815         
20816     },
20817     
20818     onRender : function(ct, position)
20819     {
20820         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20821     },
20822     
20823     setActive : function(state)
20824     {
20825         Roo.log("panel - set active " + this.tabId + "=" + state);
20826         
20827         this.active = state;
20828         if (!state) {
20829             this.el.removeClass('active');
20830             
20831         } else  if (!this.el.hasClass('active')) {
20832             this.el.addClass('active');
20833         }
20834         
20835         this.fireEvent('changed', this, state);
20836     },
20837     
20838     onClick : function(e)
20839     {
20840         e.preventDefault();
20841         
20842         if(!this.href.length){
20843             return;
20844         }
20845         
20846         window.location.href = this.href;
20847     },
20848     
20849     startX : 0,
20850     startY : 0,
20851     endX : 0,
20852     endY : 0,
20853     swiping : false,
20854     
20855     onTouchStart : function(e)
20856     {
20857         this.swiping = false;
20858         
20859         this.startX = e.browserEvent.touches[0].clientX;
20860         this.startY = e.browserEvent.touches[0].clientY;
20861     },
20862     
20863     onTouchMove : function(e)
20864     {
20865         this.swiping = true;
20866         
20867         this.endX = e.browserEvent.touches[0].clientX;
20868         this.endY = e.browserEvent.touches[0].clientY;
20869     },
20870     
20871     onTouchEnd : function(e)
20872     {
20873         if(!this.swiping){
20874             this.onClick(e);
20875             return;
20876         }
20877         
20878         var tabGroup = this.parent();
20879         
20880         if(this.endX > this.startX){ // swiping right
20881             tabGroup.showPanelPrev();
20882             return;
20883         }
20884         
20885         if(this.startX > this.endX){ // swiping left
20886             tabGroup.showPanelNext();
20887             return;
20888         }
20889     }
20890     
20891     
20892 });
20893  
20894
20895  
20896
20897  /*
20898  * - LGPL
20899  *
20900  * DateField
20901  * 
20902  */
20903
20904 /**
20905  * @class Roo.bootstrap.DateField
20906  * @extends Roo.bootstrap.Input
20907  * Bootstrap DateField class
20908  * @cfg {Number} weekStart default 0
20909  * @cfg {String} viewMode default empty, (months|years)
20910  * @cfg {String} minViewMode default empty, (months|years)
20911  * @cfg {Number} startDate default -Infinity
20912  * @cfg {Number} endDate default Infinity
20913  * @cfg {Boolean} todayHighlight default false
20914  * @cfg {Boolean} todayBtn default false
20915  * @cfg {Boolean} calendarWeeks default false
20916  * @cfg {Object} daysOfWeekDisabled default empty
20917  * @cfg {Boolean} singleMode default false (true | false)
20918  * 
20919  * @cfg {Boolean} keyboardNavigation default true
20920  * @cfg {String} language default en
20921  * 
20922  * @constructor
20923  * Create a new DateField
20924  * @param {Object} config The config object
20925  */
20926
20927 Roo.bootstrap.DateField = function(config){
20928     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20929      this.addEvents({
20930             /**
20931              * @event show
20932              * Fires when this field show.
20933              * @param {Roo.bootstrap.DateField} this
20934              * @param {Mixed} date The date value
20935              */
20936             show : true,
20937             /**
20938              * @event show
20939              * Fires when this field hide.
20940              * @param {Roo.bootstrap.DateField} this
20941              * @param {Mixed} date The date value
20942              */
20943             hide : true,
20944             /**
20945              * @event select
20946              * Fires when select a date.
20947              * @param {Roo.bootstrap.DateField} this
20948              * @param {Mixed} date The date value
20949              */
20950             select : true,
20951             /**
20952              * @event beforeselect
20953              * Fires when before select a date.
20954              * @param {Roo.bootstrap.DateField} this
20955              * @param {Mixed} date The date value
20956              */
20957             beforeselect : true
20958         });
20959 };
20960
20961 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20962     
20963     /**
20964      * @cfg {String} format
20965      * The default date format string which can be overriden for localization support.  The format must be
20966      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20967      */
20968     format : "m/d/y",
20969     /**
20970      * @cfg {String} altFormats
20971      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20972      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20973      */
20974     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20975     
20976     weekStart : 0,
20977     
20978     viewMode : '',
20979     
20980     minViewMode : '',
20981     
20982     todayHighlight : false,
20983     
20984     todayBtn: false,
20985     
20986     language: 'en',
20987     
20988     keyboardNavigation: true,
20989     
20990     calendarWeeks: false,
20991     
20992     startDate: -Infinity,
20993     
20994     endDate: Infinity,
20995     
20996     daysOfWeekDisabled: [],
20997     
20998     _events: [],
20999     
21000     singleMode : false,
21001     
21002     UTCDate: function()
21003     {
21004         return new Date(Date.UTC.apply(Date, arguments));
21005     },
21006     
21007     UTCToday: function()
21008     {
21009         var today = new Date();
21010         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21011     },
21012     
21013     getDate: function() {
21014             var d = this.getUTCDate();
21015             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21016     },
21017     
21018     getUTCDate: function() {
21019             return this.date;
21020     },
21021     
21022     setDate: function(d) {
21023             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21024     },
21025     
21026     setUTCDate: function(d) {
21027             this.date = d;
21028             this.setValue(this.formatDate(this.date));
21029     },
21030         
21031     onRender: function(ct, position)
21032     {
21033         
21034         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21035         
21036         this.language = this.language || 'en';
21037         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21038         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21039         
21040         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21041         this.format = this.format || 'm/d/y';
21042         this.isInline = false;
21043         this.isInput = true;
21044         this.component = this.el.select('.add-on', true).first() || false;
21045         this.component = (this.component && this.component.length === 0) ? false : this.component;
21046         this.hasInput = this.component && this.inputEl().length;
21047         
21048         if (typeof(this.minViewMode === 'string')) {
21049             switch (this.minViewMode) {
21050                 case 'months':
21051                     this.minViewMode = 1;
21052                     break;
21053                 case 'years':
21054                     this.minViewMode = 2;
21055                     break;
21056                 default:
21057                     this.minViewMode = 0;
21058                     break;
21059             }
21060         }
21061         
21062         if (typeof(this.viewMode === 'string')) {
21063             switch (this.viewMode) {
21064                 case 'months':
21065                     this.viewMode = 1;
21066                     break;
21067                 case 'years':
21068                     this.viewMode = 2;
21069                     break;
21070                 default:
21071                     this.viewMode = 0;
21072                     break;
21073             }
21074         }
21075                 
21076         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21077         
21078 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21079         
21080         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21081         
21082         this.picker().on('mousedown', this.onMousedown, this);
21083         this.picker().on('click', this.onClick, this);
21084         
21085         this.picker().addClass('datepicker-dropdown');
21086         
21087         this.startViewMode = this.viewMode;
21088         
21089         if(this.singleMode){
21090             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21091                 v.setVisibilityMode(Roo.Element.DISPLAY);
21092                 v.hide();
21093             });
21094             
21095             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21096                 v.setStyle('width', '189px');
21097             });
21098         }
21099         
21100         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21101             if(!this.calendarWeeks){
21102                 v.remove();
21103                 return;
21104             }
21105             
21106             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21107             v.attr('colspan', function(i, val){
21108                 return parseInt(val) + 1;
21109             });
21110         });
21111                         
21112         
21113         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21114         
21115         this.setStartDate(this.startDate);
21116         this.setEndDate(this.endDate);
21117         
21118         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21119         
21120         this.fillDow();
21121         this.fillMonths();
21122         this.update();
21123         this.showMode();
21124         
21125         if(this.isInline) {
21126             this.showPopup();
21127         }
21128     },
21129     
21130     picker : function()
21131     {
21132         return this.pickerEl;
21133 //        return this.el.select('.datepicker', true).first();
21134     },
21135     
21136     fillDow: function()
21137     {
21138         var dowCnt = this.weekStart;
21139         
21140         var dow = {
21141             tag: 'tr',
21142             cn: [
21143                 
21144             ]
21145         };
21146         
21147         if(this.calendarWeeks){
21148             dow.cn.push({
21149                 tag: 'th',
21150                 cls: 'cw',
21151                 html: '&nbsp;'
21152             })
21153         }
21154         
21155         while (dowCnt < this.weekStart + 7) {
21156             dow.cn.push({
21157                 tag: 'th',
21158                 cls: 'dow',
21159                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21160             });
21161         }
21162         
21163         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21164     },
21165     
21166     fillMonths: function()
21167     {    
21168         var i = 0;
21169         var months = this.picker().select('>.datepicker-months td', true).first();
21170         
21171         months.dom.innerHTML = '';
21172         
21173         while (i < 12) {
21174             var month = {
21175                 tag: 'span',
21176                 cls: 'month',
21177                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21178             };
21179             
21180             months.createChild(month);
21181         }
21182         
21183     },
21184     
21185     update: function()
21186     {
21187         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;
21188         
21189         if (this.date < this.startDate) {
21190             this.viewDate = new Date(this.startDate);
21191         } else if (this.date > this.endDate) {
21192             this.viewDate = new Date(this.endDate);
21193         } else {
21194             this.viewDate = new Date(this.date);
21195         }
21196         
21197         this.fill();
21198     },
21199     
21200     fill: function() 
21201     {
21202         var d = new Date(this.viewDate),
21203                 year = d.getUTCFullYear(),
21204                 month = d.getUTCMonth(),
21205                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21206                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21207                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21208                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21209                 currentDate = this.date && this.date.valueOf(),
21210                 today = this.UTCToday();
21211         
21212         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21213         
21214 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21215         
21216 //        this.picker.select('>tfoot th.today').
21217 //                                              .text(dates[this.language].today)
21218 //                                              .toggle(this.todayBtn !== false);
21219     
21220         this.updateNavArrows();
21221         this.fillMonths();
21222                                                 
21223         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21224         
21225         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21226          
21227         prevMonth.setUTCDate(day);
21228         
21229         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21230         
21231         var nextMonth = new Date(prevMonth);
21232         
21233         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21234         
21235         nextMonth = nextMonth.valueOf();
21236         
21237         var fillMonths = false;
21238         
21239         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21240         
21241         while(prevMonth.valueOf() <= nextMonth) {
21242             var clsName = '';
21243             
21244             if (prevMonth.getUTCDay() === this.weekStart) {
21245                 if(fillMonths){
21246                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21247                 }
21248                     
21249                 fillMonths = {
21250                     tag: 'tr',
21251                     cn: []
21252                 };
21253                 
21254                 if(this.calendarWeeks){
21255                     // ISO 8601: First week contains first thursday.
21256                     // ISO also states week starts on Monday, but we can be more abstract here.
21257                     var
21258                     // Start of current week: based on weekstart/current date
21259                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21260                     // Thursday of this week
21261                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21262                     // First Thursday of year, year from thursday
21263                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21264                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21265                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21266                     
21267                     fillMonths.cn.push({
21268                         tag: 'td',
21269                         cls: 'cw',
21270                         html: calWeek
21271                     });
21272                 }
21273             }
21274             
21275             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21276                 clsName += ' old';
21277             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21278                 clsName += ' new';
21279             }
21280             if (this.todayHighlight &&
21281                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21282                 prevMonth.getUTCMonth() == today.getMonth() &&
21283                 prevMonth.getUTCDate() == today.getDate()) {
21284                 clsName += ' today';
21285             }
21286             
21287             if (currentDate && prevMonth.valueOf() === currentDate) {
21288                 clsName += ' active';
21289             }
21290             
21291             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21292                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21293                     clsName += ' disabled';
21294             }
21295             
21296             fillMonths.cn.push({
21297                 tag: 'td',
21298                 cls: 'day ' + clsName,
21299                 html: prevMonth.getDate()
21300             });
21301             
21302             prevMonth.setDate(prevMonth.getDate()+1);
21303         }
21304           
21305         var currentYear = this.date && this.date.getUTCFullYear();
21306         var currentMonth = this.date && this.date.getUTCMonth();
21307         
21308         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21309         
21310         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21311             v.removeClass('active');
21312             
21313             if(currentYear === year && k === currentMonth){
21314                 v.addClass('active');
21315             }
21316             
21317             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21318                 v.addClass('disabled');
21319             }
21320             
21321         });
21322         
21323         
21324         year = parseInt(year/10, 10) * 10;
21325         
21326         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21327         
21328         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21329         
21330         year -= 1;
21331         for (var i = -1; i < 11; i++) {
21332             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21333                 tag: 'span',
21334                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21335                 html: year
21336             });
21337             
21338             year += 1;
21339         }
21340     },
21341     
21342     showMode: function(dir) 
21343     {
21344         if (dir) {
21345             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21346         }
21347         
21348         Roo.each(this.picker().select('>div',true).elements, function(v){
21349             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21350             v.hide();
21351         });
21352         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21353     },
21354     
21355     place: function()
21356     {
21357         if(this.isInline) {
21358             return;
21359         }
21360         
21361         this.picker().removeClass(['bottom', 'top']);
21362         
21363         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21364             /*
21365              * place to the top of element!
21366              *
21367              */
21368             
21369             this.picker().addClass('top');
21370             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21371             
21372             return;
21373         }
21374         
21375         this.picker().addClass('bottom');
21376         
21377         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21378     },
21379     
21380     parseDate : function(value)
21381     {
21382         if(!value || value instanceof Date){
21383             return value;
21384         }
21385         var v = Date.parseDate(value, this.format);
21386         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21387             v = Date.parseDate(value, 'Y-m-d');
21388         }
21389         if(!v && this.altFormats){
21390             if(!this.altFormatsArray){
21391                 this.altFormatsArray = this.altFormats.split("|");
21392             }
21393             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21394                 v = Date.parseDate(value, this.altFormatsArray[i]);
21395             }
21396         }
21397         return v;
21398     },
21399     
21400     formatDate : function(date, fmt)
21401     {   
21402         return (!date || !(date instanceof Date)) ?
21403         date : date.dateFormat(fmt || this.format);
21404     },
21405     
21406     onFocus : function()
21407     {
21408         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21409         this.showPopup();
21410     },
21411     
21412     onBlur : function()
21413     {
21414         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21415         
21416         var d = this.inputEl().getValue();
21417         
21418         this.setValue(d);
21419                 
21420         this.hidePopup();
21421     },
21422     
21423     showPopup : function()
21424     {
21425         this.picker().show();
21426         this.update();
21427         this.place();
21428         
21429         this.fireEvent('showpopup', this, this.date);
21430     },
21431     
21432     hidePopup : function()
21433     {
21434         if(this.isInline) {
21435             return;
21436         }
21437         this.picker().hide();
21438         this.viewMode = this.startViewMode;
21439         this.showMode();
21440         
21441         this.fireEvent('hidepopup', this, this.date);
21442         
21443     },
21444     
21445     onMousedown: function(e)
21446     {
21447         e.stopPropagation();
21448         e.preventDefault();
21449     },
21450     
21451     keyup: function(e)
21452     {
21453         Roo.bootstrap.DateField.superclass.keyup.call(this);
21454         this.update();
21455     },
21456
21457     setValue: function(v)
21458     {
21459         if(this.fireEvent('beforeselect', this, v) !== false){
21460             var d = new Date(this.parseDate(v) ).clearTime();
21461         
21462             if(isNaN(d.getTime())){
21463                 this.date = this.viewDate = '';
21464                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21465                 return;
21466             }
21467
21468             v = this.formatDate(d);
21469
21470             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21471
21472             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21473
21474             this.update();
21475
21476             this.fireEvent('select', this, this.date);
21477         }
21478     },
21479     
21480     getValue: function()
21481     {
21482         return this.formatDate(this.date);
21483     },
21484     
21485     fireKey: function(e)
21486     {
21487         if (!this.picker().isVisible()){
21488             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21489                 this.showPopup();
21490             }
21491             return;
21492         }
21493         
21494         var dateChanged = false,
21495         dir, day, month,
21496         newDate, newViewDate;
21497         
21498         switch(e.keyCode){
21499             case 27: // escape
21500                 this.hidePopup();
21501                 e.preventDefault();
21502                 break;
21503             case 37: // left
21504             case 39: // right
21505                 if (!this.keyboardNavigation) {
21506                     break;
21507                 }
21508                 dir = e.keyCode == 37 ? -1 : 1;
21509                 
21510                 if (e.ctrlKey){
21511                     newDate = this.moveYear(this.date, dir);
21512                     newViewDate = this.moveYear(this.viewDate, dir);
21513                 } else if (e.shiftKey){
21514                     newDate = this.moveMonth(this.date, dir);
21515                     newViewDate = this.moveMonth(this.viewDate, dir);
21516                 } else {
21517                     newDate = new Date(this.date);
21518                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21519                     newViewDate = new Date(this.viewDate);
21520                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21521                 }
21522                 if (this.dateWithinRange(newDate)){
21523                     this.date = newDate;
21524                     this.viewDate = newViewDate;
21525                     this.setValue(this.formatDate(this.date));
21526 //                    this.update();
21527                     e.preventDefault();
21528                     dateChanged = true;
21529                 }
21530                 break;
21531             case 38: // up
21532             case 40: // down
21533                 if (!this.keyboardNavigation) {
21534                     break;
21535                 }
21536                 dir = e.keyCode == 38 ? -1 : 1;
21537                 if (e.ctrlKey){
21538                     newDate = this.moveYear(this.date, dir);
21539                     newViewDate = this.moveYear(this.viewDate, dir);
21540                 } else if (e.shiftKey){
21541                     newDate = this.moveMonth(this.date, dir);
21542                     newViewDate = this.moveMonth(this.viewDate, dir);
21543                 } else {
21544                     newDate = new Date(this.date);
21545                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21546                     newViewDate = new Date(this.viewDate);
21547                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21548                 }
21549                 if (this.dateWithinRange(newDate)){
21550                     this.date = newDate;
21551                     this.viewDate = newViewDate;
21552                     this.setValue(this.formatDate(this.date));
21553 //                    this.update();
21554                     e.preventDefault();
21555                     dateChanged = true;
21556                 }
21557                 break;
21558             case 13: // enter
21559                 this.setValue(this.formatDate(this.date));
21560                 this.hidePopup();
21561                 e.preventDefault();
21562                 break;
21563             case 9: // tab
21564                 this.setValue(this.formatDate(this.date));
21565                 this.hidePopup();
21566                 break;
21567             case 16: // shift
21568             case 17: // ctrl
21569             case 18: // alt
21570                 break;
21571             default :
21572                 this.hidePopup();
21573                 
21574         }
21575     },
21576     
21577     
21578     onClick: function(e) 
21579     {
21580         e.stopPropagation();
21581         e.preventDefault();
21582         
21583         var target = e.getTarget();
21584         
21585         if(target.nodeName.toLowerCase() === 'i'){
21586             target = Roo.get(target).dom.parentNode;
21587         }
21588         
21589         var nodeName = target.nodeName;
21590         var className = target.className;
21591         var html = target.innerHTML;
21592         //Roo.log(nodeName);
21593         
21594         switch(nodeName.toLowerCase()) {
21595             case 'th':
21596                 switch(className) {
21597                     case 'switch':
21598                         this.showMode(1);
21599                         break;
21600                     case 'prev':
21601                     case 'next':
21602                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21603                         switch(this.viewMode){
21604                                 case 0:
21605                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21606                                         break;
21607                                 case 1:
21608                                 case 2:
21609                                         this.viewDate = this.moveYear(this.viewDate, dir);
21610                                         break;
21611                         }
21612                         this.fill();
21613                         break;
21614                     case 'today':
21615                         var date = new Date();
21616                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21617 //                        this.fill()
21618                         this.setValue(this.formatDate(this.date));
21619                         
21620                         this.hidePopup();
21621                         break;
21622                 }
21623                 break;
21624             case 'span':
21625                 if (className.indexOf('disabled') < 0) {
21626                     this.viewDate.setUTCDate(1);
21627                     if (className.indexOf('month') > -1) {
21628                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21629                     } else {
21630                         var year = parseInt(html, 10) || 0;
21631                         this.viewDate.setUTCFullYear(year);
21632                         
21633                     }
21634                     
21635                     if(this.singleMode){
21636                         this.setValue(this.formatDate(this.viewDate));
21637                         this.hidePopup();
21638                         return;
21639                     }
21640                     
21641                     this.showMode(-1);
21642                     this.fill();
21643                 }
21644                 break;
21645                 
21646             case 'td':
21647                 //Roo.log(className);
21648                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21649                     var day = parseInt(html, 10) || 1;
21650                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
21651                         month = (this.viewDate || new Date()).getUTCMonth();
21652
21653                     if (className.indexOf('old') > -1) {
21654                         if(month === 0 ){
21655                             month = 11;
21656                             year -= 1;
21657                         }else{
21658                             month -= 1;
21659                         }
21660                     } else if (className.indexOf('new') > -1) {
21661                         if (month == 11) {
21662                             month = 0;
21663                             year += 1;
21664                         } else {
21665                             month += 1;
21666                         }
21667                     }
21668                     //Roo.log([year,month,day]);
21669                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21670                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21671 //                    this.fill();
21672                     //Roo.log(this.formatDate(this.date));
21673                     this.setValue(this.formatDate(this.date));
21674                     this.hidePopup();
21675                 }
21676                 break;
21677         }
21678     },
21679     
21680     setStartDate: function(startDate)
21681     {
21682         this.startDate = startDate || -Infinity;
21683         if (this.startDate !== -Infinity) {
21684             this.startDate = this.parseDate(this.startDate);
21685         }
21686         this.update();
21687         this.updateNavArrows();
21688     },
21689
21690     setEndDate: function(endDate)
21691     {
21692         this.endDate = endDate || Infinity;
21693         if (this.endDate !== Infinity) {
21694             this.endDate = this.parseDate(this.endDate);
21695         }
21696         this.update();
21697         this.updateNavArrows();
21698     },
21699     
21700     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21701     {
21702         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21703         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21704             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21705         }
21706         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21707             return parseInt(d, 10);
21708         });
21709         this.update();
21710         this.updateNavArrows();
21711     },
21712     
21713     updateNavArrows: function() 
21714     {
21715         if(this.singleMode){
21716             return;
21717         }
21718         
21719         var d = new Date(this.viewDate),
21720         year = d.getUTCFullYear(),
21721         month = d.getUTCMonth();
21722         
21723         Roo.each(this.picker().select('.prev', true).elements, function(v){
21724             v.show();
21725             switch (this.viewMode) {
21726                 case 0:
21727
21728                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21729                         v.hide();
21730                     }
21731                     break;
21732                 case 1:
21733                 case 2:
21734                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21735                         v.hide();
21736                     }
21737                     break;
21738             }
21739         });
21740         
21741         Roo.each(this.picker().select('.next', true).elements, function(v){
21742             v.show();
21743             switch (this.viewMode) {
21744                 case 0:
21745
21746                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21747                         v.hide();
21748                     }
21749                     break;
21750                 case 1:
21751                 case 2:
21752                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21753                         v.hide();
21754                     }
21755                     break;
21756             }
21757         })
21758     },
21759     
21760     moveMonth: function(date, dir)
21761     {
21762         if (!dir) {
21763             return date;
21764         }
21765         var new_date = new Date(date.valueOf()),
21766         day = new_date.getUTCDate(),
21767         month = new_date.getUTCMonth(),
21768         mag = Math.abs(dir),
21769         new_month, test;
21770         dir = dir > 0 ? 1 : -1;
21771         if (mag == 1){
21772             test = dir == -1
21773             // If going back one month, make sure month is not current month
21774             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21775             ? function(){
21776                 return new_date.getUTCMonth() == month;
21777             }
21778             // If going forward one month, make sure month is as expected
21779             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21780             : function(){
21781                 return new_date.getUTCMonth() != new_month;
21782             };
21783             new_month = month + dir;
21784             new_date.setUTCMonth(new_month);
21785             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21786             if (new_month < 0 || new_month > 11) {
21787                 new_month = (new_month + 12) % 12;
21788             }
21789         } else {
21790             // For magnitudes >1, move one month at a time...
21791             for (var i=0; i<mag; i++) {
21792                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21793                 new_date = this.moveMonth(new_date, dir);
21794             }
21795             // ...then reset the day, keeping it in the new month
21796             new_month = new_date.getUTCMonth();
21797             new_date.setUTCDate(day);
21798             test = function(){
21799                 return new_month != new_date.getUTCMonth();
21800             };
21801         }
21802         // Common date-resetting loop -- if date is beyond end of month, make it
21803         // end of month
21804         while (test()){
21805             new_date.setUTCDate(--day);
21806             new_date.setUTCMonth(new_month);
21807         }
21808         return new_date;
21809     },
21810
21811     moveYear: function(date, dir)
21812     {
21813         return this.moveMonth(date, dir*12);
21814     },
21815
21816     dateWithinRange: function(date)
21817     {
21818         return date >= this.startDate && date <= this.endDate;
21819     },
21820
21821     
21822     remove: function() 
21823     {
21824         this.picker().remove();
21825     },
21826     
21827     validateValue : function(value)
21828     {
21829         if(this.getVisibilityEl().hasClass('hidden')){
21830             return true;
21831         }
21832         
21833         if(value.length < 1)  {
21834             if(this.allowBlank){
21835                 return true;
21836             }
21837             return false;
21838         }
21839         
21840         if(value.length < this.minLength){
21841             return false;
21842         }
21843         if(value.length > this.maxLength){
21844             return false;
21845         }
21846         if(this.vtype){
21847             var vt = Roo.form.VTypes;
21848             if(!vt[this.vtype](value, this)){
21849                 return false;
21850             }
21851         }
21852         if(typeof this.validator == "function"){
21853             var msg = this.validator(value);
21854             if(msg !== true){
21855                 return false;
21856             }
21857         }
21858         
21859         if(this.regex && !this.regex.test(value)){
21860             return false;
21861         }
21862         
21863         if(typeof(this.parseDate(value)) == 'undefined'){
21864             return false;
21865         }
21866         
21867         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21868             return false;
21869         }      
21870         
21871         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21872             return false;
21873         } 
21874         
21875         
21876         return true;
21877     },
21878     
21879     reset : function()
21880     {
21881         this.date = this.viewDate = '';
21882         
21883         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21884     }
21885    
21886 });
21887
21888 Roo.apply(Roo.bootstrap.DateField,  {
21889     
21890     head : {
21891         tag: 'thead',
21892         cn: [
21893         {
21894             tag: 'tr',
21895             cn: [
21896             {
21897                 tag: 'th',
21898                 cls: 'prev',
21899                 html: '<i class="fa fa-arrow-left"/>'
21900             },
21901             {
21902                 tag: 'th',
21903                 cls: 'switch',
21904                 colspan: '5'
21905             },
21906             {
21907                 tag: 'th',
21908                 cls: 'next',
21909                 html: '<i class="fa fa-arrow-right"/>'
21910             }
21911
21912             ]
21913         }
21914         ]
21915     },
21916     
21917     content : {
21918         tag: 'tbody',
21919         cn: [
21920         {
21921             tag: 'tr',
21922             cn: [
21923             {
21924                 tag: 'td',
21925                 colspan: '7'
21926             }
21927             ]
21928         }
21929         ]
21930     },
21931     
21932     footer : {
21933         tag: 'tfoot',
21934         cn: [
21935         {
21936             tag: 'tr',
21937             cn: [
21938             {
21939                 tag: 'th',
21940                 colspan: '7',
21941                 cls: 'today'
21942             }
21943                     
21944             ]
21945         }
21946         ]
21947     },
21948     
21949     dates:{
21950         en: {
21951             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21952             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21953             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21954             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21955             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21956             today: "Today"
21957         }
21958     },
21959     
21960     modes: [
21961     {
21962         clsName: 'days',
21963         navFnc: 'Month',
21964         navStep: 1
21965     },
21966     {
21967         clsName: 'months',
21968         navFnc: 'FullYear',
21969         navStep: 1
21970     },
21971     {
21972         clsName: 'years',
21973         navFnc: 'FullYear',
21974         navStep: 10
21975     }]
21976 });
21977
21978 Roo.apply(Roo.bootstrap.DateField,  {
21979   
21980     template : {
21981         tag: 'div',
21982         cls: 'datepicker dropdown-menu roo-dynamic shadow',
21983         cn: [
21984         {
21985             tag: 'div',
21986             cls: 'datepicker-days',
21987             cn: [
21988             {
21989                 tag: 'table',
21990                 cls: 'table-condensed',
21991                 cn:[
21992                 Roo.bootstrap.DateField.head,
21993                 {
21994                     tag: 'tbody'
21995                 },
21996                 Roo.bootstrap.DateField.footer
21997                 ]
21998             }
21999             ]
22000         },
22001         {
22002             tag: 'div',
22003             cls: 'datepicker-months',
22004             cn: [
22005             {
22006                 tag: 'table',
22007                 cls: 'table-condensed',
22008                 cn:[
22009                 Roo.bootstrap.DateField.head,
22010                 Roo.bootstrap.DateField.content,
22011                 Roo.bootstrap.DateField.footer
22012                 ]
22013             }
22014             ]
22015         },
22016         {
22017             tag: 'div',
22018             cls: 'datepicker-years',
22019             cn: [
22020             {
22021                 tag: 'table',
22022                 cls: 'table-condensed',
22023                 cn:[
22024                 Roo.bootstrap.DateField.head,
22025                 Roo.bootstrap.DateField.content,
22026                 Roo.bootstrap.DateField.footer
22027                 ]
22028             }
22029             ]
22030         }
22031         ]
22032     }
22033 });
22034
22035  
22036
22037  /*
22038  * - LGPL
22039  *
22040  * TimeField
22041  * 
22042  */
22043
22044 /**
22045  * @class Roo.bootstrap.TimeField
22046  * @extends Roo.bootstrap.Input
22047  * Bootstrap DateField class
22048  * 
22049  * 
22050  * @constructor
22051  * Create a new TimeField
22052  * @param {Object} config The config object
22053  */
22054
22055 Roo.bootstrap.TimeField = function(config){
22056     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22057     this.addEvents({
22058             /**
22059              * @event show
22060              * Fires when this field show.
22061              * @param {Roo.bootstrap.DateField} thisthis
22062              * @param {Mixed} date The date value
22063              */
22064             show : true,
22065             /**
22066              * @event show
22067              * Fires when this field hide.
22068              * @param {Roo.bootstrap.DateField} this
22069              * @param {Mixed} date The date value
22070              */
22071             hide : true,
22072             /**
22073              * @event select
22074              * Fires when select a date.
22075              * @param {Roo.bootstrap.DateField} this
22076              * @param {Mixed} date The date value
22077              */
22078             select : true
22079         });
22080 };
22081
22082 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
22083     
22084     /**
22085      * @cfg {String} format
22086      * The default time format string which can be overriden for localization support.  The format must be
22087      * valid according to {@link Date#parseDate} (defaults to 'H:i').
22088      */
22089     format : "H:i",
22090
22091     getAutoCreate : function()
22092     {
22093         this.after = '<i class="fa far fa-clock"></i>';
22094         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22095         
22096          
22097     },
22098     onRender: function(ct, position)
22099     {
22100         
22101         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22102                 
22103         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22104         
22105         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22106         
22107         this.pop = this.picker().select('>.datepicker-time',true).first();
22108         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22109         
22110         this.picker().on('mousedown', this.onMousedown, this);
22111         this.picker().on('click', this.onClick, this);
22112         
22113         this.picker().addClass('datepicker-dropdown');
22114     
22115         this.fillTime();
22116         this.update();
22117             
22118         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22119         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22120         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22121         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22122         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22123         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22124
22125     },
22126     
22127     fireKey: function(e){
22128         if (!this.picker().isVisible()){
22129             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22130                 this.show();
22131             }
22132             return;
22133         }
22134
22135         e.preventDefault();
22136         
22137         switch(e.keyCode){
22138             case 27: // escape
22139                 this.hide();
22140                 break;
22141             case 37: // left
22142             case 39: // right
22143                 this.onTogglePeriod();
22144                 break;
22145             case 38: // up
22146                 this.onIncrementMinutes();
22147                 break;
22148             case 40: // down
22149                 this.onDecrementMinutes();
22150                 break;
22151             case 13: // enter
22152             case 9: // tab
22153                 this.setTime();
22154                 break;
22155         }
22156     },
22157     
22158     onClick: function(e) {
22159         e.stopPropagation();
22160         e.preventDefault();
22161     },
22162     
22163     picker : function()
22164     {
22165         return this.pickerEl;
22166     },
22167     
22168     fillTime: function()
22169     {    
22170         var time = this.pop.select('tbody', true).first();
22171         
22172         time.dom.innerHTML = '';
22173         
22174         time.createChild({
22175             tag: 'tr',
22176             cn: [
22177                 {
22178                     tag: 'td',
22179                     cn: [
22180                         {
22181                             tag: 'a',
22182                             href: '#',
22183                             cls: 'btn',
22184                             cn: [
22185                                 {
22186                                     tag: 'i',
22187                                     cls: 'hours-up fa fas fa-chevron-up'
22188                                 }
22189                             ]
22190                         } 
22191                     ]
22192                 },
22193                 {
22194                     tag: 'td',
22195                     cls: 'separator'
22196                 },
22197                 {
22198                     tag: 'td',
22199                     cn: [
22200                         {
22201                             tag: 'a',
22202                             href: '#',
22203                             cls: 'btn',
22204                             cn: [
22205                                 {
22206                                     tag: 'i',
22207                                     cls: 'minutes-up fa fas fa-chevron-up'
22208                                 }
22209                             ]
22210                         }
22211                     ]
22212                 },
22213                 {
22214                     tag: 'td',
22215                     cls: 'separator'
22216                 }
22217             ]
22218         });
22219         
22220         time.createChild({
22221             tag: 'tr',
22222             cn: [
22223                 {
22224                     tag: 'td',
22225                     cn: [
22226                         {
22227                             tag: 'span',
22228                             cls: 'timepicker-hour',
22229                             html: '00'
22230                         }  
22231                     ]
22232                 },
22233                 {
22234                     tag: 'td',
22235                     cls: 'separator',
22236                     html: ':'
22237                 },
22238                 {
22239                     tag: 'td',
22240                     cn: [
22241                         {
22242                             tag: 'span',
22243                             cls: 'timepicker-minute',
22244                             html: '00'
22245                         }  
22246                     ]
22247                 },
22248                 {
22249                     tag: 'td',
22250                     cls: 'separator'
22251                 },
22252                 {
22253                     tag: 'td',
22254                     cn: [
22255                         {
22256                             tag: 'button',
22257                             type: 'button',
22258                             cls: 'btn btn-primary period',
22259                             html: 'AM'
22260                             
22261                         }
22262                     ]
22263                 }
22264             ]
22265         });
22266         
22267         time.createChild({
22268             tag: 'tr',
22269             cn: [
22270                 {
22271                     tag: 'td',
22272                     cn: [
22273                         {
22274                             tag: 'a',
22275                             href: '#',
22276                             cls: 'btn',
22277                             cn: [
22278                                 {
22279                                     tag: 'span',
22280                                     cls: 'hours-down fa fas fa-chevron-down'
22281                                 }
22282                             ]
22283                         }
22284                     ]
22285                 },
22286                 {
22287                     tag: 'td',
22288                     cls: 'separator'
22289                 },
22290                 {
22291                     tag: 'td',
22292                     cn: [
22293                         {
22294                             tag: 'a',
22295                             href: '#',
22296                             cls: 'btn',
22297                             cn: [
22298                                 {
22299                                     tag: 'span',
22300                                     cls: 'minutes-down fa fas fa-chevron-down'
22301                                 }
22302                             ]
22303                         }
22304                     ]
22305                 },
22306                 {
22307                     tag: 'td',
22308                     cls: 'separator'
22309                 }
22310             ]
22311         });
22312         
22313     },
22314     
22315     update: function()
22316     {
22317         
22318         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22319         
22320         this.fill();
22321     },
22322     
22323     fill: function() 
22324     {
22325         var hours = this.time.getHours();
22326         var minutes = this.time.getMinutes();
22327         var period = 'AM';
22328         
22329         if(hours > 11){
22330             period = 'PM';
22331         }
22332         
22333         if(hours == 0){
22334             hours = 12;
22335         }
22336         
22337         
22338         if(hours > 12){
22339             hours = hours - 12;
22340         }
22341         
22342         if(hours < 10){
22343             hours = '0' + hours;
22344         }
22345         
22346         if(minutes < 10){
22347             minutes = '0' + minutes;
22348         }
22349         
22350         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22351         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22352         this.pop.select('button', true).first().dom.innerHTML = period;
22353         
22354     },
22355     
22356     place: function()
22357     {   
22358         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22359         
22360         var cls = ['bottom'];
22361         
22362         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22363             cls.pop();
22364             cls.push('top');
22365         }
22366         
22367         cls.push('right');
22368         
22369         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22370             cls.pop();
22371             cls.push('left');
22372         }
22373         //this.picker().setXY(20000,20000);
22374         this.picker().addClass(cls.join('-'));
22375         
22376         var _this = this;
22377         
22378         Roo.each(cls, function(c){
22379             if(c == 'bottom'){
22380                 (function() {
22381                  //  
22382                 }).defer(200);
22383                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
22384                 //_this.picker().setTop(_this.inputEl().getHeight());
22385                 return;
22386             }
22387             if(c == 'top'){
22388                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
22389                 
22390                 //_this.picker().setTop(0 - _this.picker().getHeight());
22391                 return;
22392             }
22393             /*
22394             if(c == 'left'){
22395                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22396                 return;
22397             }
22398             if(c == 'right'){
22399                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22400                 return;
22401             }
22402             */
22403         });
22404         
22405     },
22406   
22407     onFocus : function()
22408     {
22409         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22410         this.show();
22411     },
22412     
22413     onBlur : function()
22414     {
22415         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22416         this.hide();
22417     },
22418     
22419     show : function()
22420     {
22421         this.picker().show();
22422         this.pop.show();
22423         this.update();
22424         this.place();
22425         
22426         this.fireEvent('show', this, this.date);
22427     },
22428     
22429     hide : function()
22430     {
22431         this.picker().hide();
22432         this.pop.hide();
22433         
22434         this.fireEvent('hide', this, this.date);
22435     },
22436     
22437     setTime : function()
22438     {
22439         this.hide();
22440         this.setValue(this.time.format(this.format));
22441         
22442         this.fireEvent('select', this, this.date);
22443         
22444         
22445     },
22446     
22447     onMousedown: function(e){
22448         e.stopPropagation();
22449         e.preventDefault();
22450     },
22451     
22452     onIncrementHours: function()
22453     {
22454         Roo.log('onIncrementHours');
22455         this.time = this.time.add(Date.HOUR, 1);
22456         this.update();
22457         
22458     },
22459     
22460     onDecrementHours: function()
22461     {
22462         Roo.log('onDecrementHours');
22463         this.time = this.time.add(Date.HOUR, -1);
22464         this.update();
22465     },
22466     
22467     onIncrementMinutes: function()
22468     {
22469         Roo.log('onIncrementMinutes');
22470         this.time = this.time.add(Date.MINUTE, 1);
22471         this.update();
22472     },
22473     
22474     onDecrementMinutes: function()
22475     {
22476         Roo.log('onDecrementMinutes');
22477         this.time = this.time.add(Date.MINUTE, -1);
22478         this.update();
22479     },
22480     
22481     onTogglePeriod: function()
22482     {
22483         Roo.log('onTogglePeriod');
22484         this.time = this.time.add(Date.HOUR, 12);
22485         this.update();
22486     }
22487     
22488    
22489 });
22490  
22491
22492 Roo.apply(Roo.bootstrap.TimeField,  {
22493   
22494     template : {
22495         tag: 'div',
22496         cls: 'datepicker dropdown-menu',
22497         cn: [
22498             {
22499                 tag: 'div',
22500                 cls: 'datepicker-time',
22501                 cn: [
22502                 {
22503                     tag: 'table',
22504                     cls: 'table-condensed',
22505                     cn:[
22506                         {
22507                             tag: 'tbody',
22508                             cn: [
22509                                 {
22510                                     tag: 'tr',
22511                                     cn: [
22512                                     {
22513                                         tag: 'td',
22514                                         colspan: '7'
22515                                     }
22516                                     ]
22517                                 }
22518                             ]
22519                         },
22520                         {
22521                             tag: 'tfoot',
22522                             cn: [
22523                                 {
22524                                     tag: 'tr',
22525                                     cn: [
22526                                     {
22527                                         tag: 'th',
22528                                         colspan: '7',
22529                                         cls: '',
22530                                         cn: [
22531                                             {
22532                                                 tag: 'button',
22533                                                 cls: 'btn btn-info ok',
22534                                                 html: 'OK'
22535                                             }
22536                                         ]
22537                                     }
22538                     
22539                                     ]
22540                                 }
22541                             ]
22542                         }
22543                     ]
22544                 }
22545                 ]
22546             }
22547         ]
22548     }
22549 });
22550
22551  
22552
22553  /*
22554  * - LGPL
22555  *
22556  * MonthField
22557  * 
22558  */
22559
22560 /**
22561  * @class Roo.bootstrap.MonthField
22562  * @extends Roo.bootstrap.Input
22563  * Bootstrap MonthField class
22564  * 
22565  * @cfg {String} language default en
22566  * 
22567  * @constructor
22568  * Create a new MonthField
22569  * @param {Object} config The config object
22570  */
22571
22572 Roo.bootstrap.MonthField = function(config){
22573     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22574     
22575     this.addEvents({
22576         /**
22577          * @event show
22578          * Fires when this field show.
22579          * @param {Roo.bootstrap.MonthField} this
22580          * @param {Mixed} date The date value
22581          */
22582         show : true,
22583         /**
22584          * @event show
22585          * Fires when this field hide.
22586          * @param {Roo.bootstrap.MonthField} this
22587          * @param {Mixed} date The date value
22588          */
22589         hide : true,
22590         /**
22591          * @event select
22592          * Fires when select a date.
22593          * @param {Roo.bootstrap.MonthField} this
22594          * @param {String} oldvalue The old value
22595          * @param {String} newvalue The new value
22596          */
22597         select : true
22598     });
22599 };
22600
22601 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22602     
22603     onRender: function(ct, position)
22604     {
22605         
22606         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22607         
22608         this.language = this.language || 'en';
22609         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22610         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22611         
22612         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22613         this.isInline = false;
22614         this.isInput = true;
22615         this.component = this.el.select('.add-on', true).first() || false;
22616         this.component = (this.component && this.component.length === 0) ? false : this.component;
22617         this.hasInput = this.component && this.inputEL().length;
22618         
22619         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22620         
22621         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22622         
22623         this.picker().on('mousedown', this.onMousedown, this);
22624         this.picker().on('click', this.onClick, this);
22625         
22626         this.picker().addClass('datepicker-dropdown');
22627         
22628         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22629             v.setStyle('width', '189px');
22630         });
22631         
22632         this.fillMonths();
22633         
22634         this.update();
22635         
22636         if(this.isInline) {
22637             this.show();
22638         }
22639         
22640     },
22641     
22642     setValue: function(v, suppressEvent)
22643     {   
22644         var o = this.getValue();
22645         
22646         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22647         
22648         this.update();
22649
22650         if(suppressEvent !== true){
22651             this.fireEvent('select', this, o, v);
22652         }
22653         
22654     },
22655     
22656     getValue: function()
22657     {
22658         return this.value;
22659     },
22660     
22661     onClick: function(e) 
22662     {
22663         e.stopPropagation();
22664         e.preventDefault();
22665         
22666         var target = e.getTarget();
22667         
22668         if(target.nodeName.toLowerCase() === 'i'){
22669             target = Roo.get(target).dom.parentNode;
22670         }
22671         
22672         var nodeName = target.nodeName;
22673         var className = target.className;
22674         var html = target.innerHTML;
22675         
22676         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22677             return;
22678         }
22679         
22680         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22681         
22682         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22683         
22684         this.hide();
22685                         
22686     },
22687     
22688     picker : function()
22689     {
22690         return this.pickerEl;
22691     },
22692     
22693     fillMonths: function()
22694     {    
22695         var i = 0;
22696         var months = this.picker().select('>.datepicker-months td', true).first();
22697         
22698         months.dom.innerHTML = '';
22699         
22700         while (i < 12) {
22701             var month = {
22702                 tag: 'span',
22703                 cls: 'month',
22704                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22705             };
22706             
22707             months.createChild(month);
22708         }
22709         
22710     },
22711     
22712     update: function()
22713     {
22714         var _this = this;
22715         
22716         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22717             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22718         }
22719         
22720         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22721             e.removeClass('active');
22722             
22723             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22724                 e.addClass('active');
22725             }
22726         })
22727     },
22728     
22729     place: function()
22730     {
22731         if(this.isInline) {
22732             return;
22733         }
22734         
22735         this.picker().removeClass(['bottom', 'top']);
22736         
22737         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22738             /*
22739              * place to the top of element!
22740              *
22741              */
22742             
22743             this.picker().addClass('top');
22744             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22745             
22746             return;
22747         }
22748         
22749         this.picker().addClass('bottom');
22750         
22751         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22752     },
22753     
22754     onFocus : function()
22755     {
22756         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22757         this.show();
22758     },
22759     
22760     onBlur : function()
22761     {
22762         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22763         
22764         var d = this.inputEl().getValue();
22765         
22766         this.setValue(d);
22767                 
22768         this.hide();
22769     },
22770     
22771     show : function()
22772     {
22773         this.picker().show();
22774         this.picker().select('>.datepicker-months', true).first().show();
22775         this.update();
22776         this.place();
22777         
22778         this.fireEvent('show', this, this.date);
22779     },
22780     
22781     hide : function()
22782     {
22783         if(this.isInline) {
22784             return;
22785         }
22786         this.picker().hide();
22787         this.fireEvent('hide', this, this.date);
22788         
22789     },
22790     
22791     onMousedown: function(e)
22792     {
22793         e.stopPropagation();
22794         e.preventDefault();
22795     },
22796     
22797     keyup: function(e)
22798     {
22799         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22800         this.update();
22801     },
22802
22803     fireKey: function(e)
22804     {
22805         if (!this.picker().isVisible()){
22806             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22807                 this.show();
22808             }
22809             return;
22810         }
22811         
22812         var dir;
22813         
22814         switch(e.keyCode){
22815             case 27: // escape
22816                 this.hide();
22817                 e.preventDefault();
22818                 break;
22819             case 37: // left
22820             case 39: // right
22821                 dir = e.keyCode == 37 ? -1 : 1;
22822                 
22823                 this.vIndex = this.vIndex + dir;
22824                 
22825                 if(this.vIndex < 0){
22826                     this.vIndex = 0;
22827                 }
22828                 
22829                 if(this.vIndex > 11){
22830                     this.vIndex = 11;
22831                 }
22832                 
22833                 if(isNaN(this.vIndex)){
22834                     this.vIndex = 0;
22835                 }
22836                 
22837                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22838                 
22839                 break;
22840             case 38: // up
22841             case 40: // down
22842                 
22843                 dir = e.keyCode == 38 ? -1 : 1;
22844                 
22845                 this.vIndex = this.vIndex + dir * 4;
22846                 
22847                 if(this.vIndex < 0){
22848                     this.vIndex = 0;
22849                 }
22850                 
22851                 if(this.vIndex > 11){
22852                     this.vIndex = 11;
22853                 }
22854                 
22855                 if(isNaN(this.vIndex)){
22856                     this.vIndex = 0;
22857                 }
22858                 
22859                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22860                 break;
22861                 
22862             case 13: // enter
22863                 
22864                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22865                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22866                 }
22867                 
22868                 this.hide();
22869                 e.preventDefault();
22870                 break;
22871             case 9: // tab
22872                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22873                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22874                 }
22875                 this.hide();
22876                 break;
22877             case 16: // shift
22878             case 17: // ctrl
22879             case 18: // alt
22880                 break;
22881             default :
22882                 this.hide();
22883                 
22884         }
22885     },
22886     
22887     remove: function() 
22888     {
22889         this.picker().remove();
22890     }
22891    
22892 });
22893
22894 Roo.apply(Roo.bootstrap.MonthField,  {
22895     
22896     content : {
22897         tag: 'tbody',
22898         cn: [
22899         {
22900             tag: 'tr',
22901             cn: [
22902             {
22903                 tag: 'td',
22904                 colspan: '7'
22905             }
22906             ]
22907         }
22908         ]
22909     },
22910     
22911     dates:{
22912         en: {
22913             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22914             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22915         }
22916     }
22917 });
22918
22919 Roo.apply(Roo.bootstrap.MonthField,  {
22920   
22921     template : {
22922         tag: 'div',
22923         cls: 'datepicker dropdown-menu roo-dynamic',
22924         cn: [
22925             {
22926                 tag: 'div',
22927                 cls: 'datepicker-months',
22928                 cn: [
22929                 {
22930                     tag: 'table',
22931                     cls: 'table-condensed',
22932                     cn:[
22933                         Roo.bootstrap.DateField.content
22934                     ]
22935                 }
22936                 ]
22937             }
22938         ]
22939     }
22940 });
22941
22942  
22943
22944  
22945  /*
22946  * - LGPL
22947  *
22948  * CheckBox
22949  * 
22950  */
22951
22952 /**
22953  * @class Roo.bootstrap.CheckBox
22954  * @extends Roo.bootstrap.Input
22955  * Bootstrap CheckBox class
22956  * 
22957  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22958  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22959  * @cfg {String} boxLabel The text that appears beside the checkbox
22960  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22961  * @cfg {Boolean} checked initnal the element
22962  * @cfg {Boolean} inline inline the element (default false)
22963  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22964  * @cfg {String} tooltip label tooltip
22965  * 
22966  * @constructor
22967  * Create a new CheckBox
22968  * @param {Object} config The config object
22969  */
22970
22971 Roo.bootstrap.CheckBox = function(config){
22972     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22973    
22974     this.addEvents({
22975         /**
22976         * @event check
22977         * Fires when the element is checked or unchecked.
22978         * @param {Roo.bootstrap.CheckBox} this This input
22979         * @param {Boolean} checked The new checked value
22980         */
22981        check : true,
22982        /**
22983         * @event click
22984         * Fires when the element is click.
22985         * @param {Roo.bootstrap.CheckBox} this This input
22986         */
22987        click : true
22988     });
22989     
22990 };
22991
22992 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
22993   
22994     inputType: 'checkbox',
22995     inputValue: 1,
22996     valueOff: 0,
22997     boxLabel: false,
22998     checked: false,
22999     weight : false,
23000     inline: false,
23001     tooltip : '',
23002     
23003     // checkbox success does not make any sense really.. 
23004     invalidClass : "",
23005     validClass : "",
23006     
23007     
23008     getAutoCreate : function()
23009     {
23010         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23011         
23012         var id = Roo.id();
23013         
23014         var cfg = {};
23015         
23016         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23017         
23018         if(this.inline){
23019             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
23020         }
23021         
23022         var input =  {
23023             tag: 'input',
23024             id : id,
23025             type : this.inputType,
23026             value : this.inputValue,
23027             cls : 'roo-' + this.inputType, //'form-box',
23028             placeholder : this.placeholder || ''
23029             
23030         };
23031         
23032         if(this.inputType != 'radio'){
23033             var hidden =  {
23034                 tag: 'input',
23035                 type : 'hidden',
23036                 cls : 'roo-hidden-value',
23037                 value : this.checked ? this.inputValue : this.valueOff
23038             };
23039         }
23040         
23041             
23042         if (this.weight) { // Validity check?
23043             cfg.cls += " " + this.inputType + "-" + this.weight;
23044         }
23045         
23046         if (this.disabled) {
23047             input.disabled=true;
23048         }
23049         
23050         if(this.checked){
23051             input.checked = this.checked;
23052         }
23053         
23054         if (this.name) {
23055             
23056             input.name = this.name;
23057             
23058             if(this.inputType != 'radio'){
23059                 hidden.name = this.name;
23060                 input.name = '_hidden_' + this.name;
23061             }
23062         }
23063         
23064         if (this.size) {
23065             input.cls += ' input-' + this.size;
23066         }
23067         
23068         var settings=this;
23069         
23070         ['xs','sm','md','lg'].map(function(size){
23071             if (settings[size]) {
23072                 cfg.cls += ' col-' + size + '-' + settings[size];
23073             }
23074         });
23075         
23076         var inputblock = input;
23077          
23078         if (this.before || this.after) {
23079             
23080             inputblock = {
23081                 cls : 'input-group',
23082                 cn :  [] 
23083             };
23084             
23085             if (this.before) {
23086                 inputblock.cn.push({
23087                     tag :'span',
23088                     cls : 'input-group-addon',
23089                     html : this.before
23090                 });
23091             }
23092             
23093             inputblock.cn.push(input);
23094             
23095             if(this.inputType != 'radio'){
23096                 inputblock.cn.push(hidden);
23097             }
23098             
23099             if (this.after) {
23100                 inputblock.cn.push({
23101                     tag :'span',
23102                     cls : 'input-group-addon',
23103                     html : this.after
23104                 });
23105             }
23106             
23107         }
23108         var boxLabelCfg = false;
23109         
23110         if(this.boxLabel){
23111            
23112             boxLabelCfg = {
23113                 tag: 'label',
23114                 //'for': id, // box label is handled by onclick - so no for...
23115                 cls: 'box-label',
23116                 html: this.boxLabel
23117             };
23118             if(this.tooltip){
23119                 boxLabelCfg.tooltip = this.tooltip;
23120             }
23121              
23122         }
23123         
23124         
23125         if (align ==='left' && this.fieldLabel.length) {
23126 //                Roo.log("left and has label");
23127             cfg.cn = [
23128                 {
23129                     tag: 'label',
23130                     'for' :  id,
23131                     cls : 'control-label',
23132                     html : this.fieldLabel
23133                 },
23134                 {
23135                     cls : "", 
23136                     cn: [
23137                         inputblock
23138                     ]
23139                 }
23140             ];
23141             
23142             if (boxLabelCfg) {
23143                 cfg.cn[1].cn.push(boxLabelCfg);
23144             }
23145             
23146             if(this.labelWidth > 12){
23147                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23148             }
23149             
23150             if(this.labelWidth < 13 && this.labelmd == 0){
23151                 this.labelmd = this.labelWidth;
23152             }
23153             
23154             if(this.labellg > 0){
23155                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23156                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23157             }
23158             
23159             if(this.labelmd > 0){
23160                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23161                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23162             }
23163             
23164             if(this.labelsm > 0){
23165                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23166                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23167             }
23168             
23169             if(this.labelxs > 0){
23170                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23171                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23172             }
23173             
23174         } else if ( this.fieldLabel.length) {
23175 //                Roo.log(" label");
23176                 cfg.cn = [
23177                    
23178                     {
23179                         tag: this.boxLabel ? 'span' : 'label',
23180                         'for': id,
23181                         cls: 'control-label box-input-label',
23182                         //cls : 'input-group-addon',
23183                         html : this.fieldLabel
23184                     },
23185                     
23186                     inputblock
23187                     
23188                 ];
23189                 if (boxLabelCfg) {
23190                     cfg.cn.push(boxLabelCfg);
23191                 }
23192
23193         } else {
23194             
23195 //                Roo.log(" no label && no align");
23196                 cfg.cn = [  inputblock ] ;
23197                 if (boxLabelCfg) {
23198                     cfg.cn.push(boxLabelCfg);
23199                 }
23200
23201                 
23202         }
23203         
23204        
23205         
23206         if(this.inputType != 'radio'){
23207             cfg.cn.push(hidden);
23208         }
23209         
23210         return cfg;
23211         
23212     },
23213     
23214     /**
23215      * return the real input element.
23216      */
23217     inputEl: function ()
23218     {
23219         return this.el.select('input.roo-' + this.inputType,true).first();
23220     },
23221     hiddenEl: function ()
23222     {
23223         return this.el.select('input.roo-hidden-value',true).first();
23224     },
23225     
23226     labelEl: function()
23227     {
23228         return this.el.select('label.control-label',true).first();
23229     },
23230     /* depricated... */
23231     
23232     label: function()
23233     {
23234         return this.labelEl();
23235     },
23236     
23237     boxLabelEl: function()
23238     {
23239         return this.el.select('label.box-label',true).first();
23240     },
23241     
23242     initEvents : function()
23243     {
23244 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23245         
23246         this.inputEl().on('click', this.onClick,  this);
23247         
23248         if (this.boxLabel) { 
23249             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23250         }
23251         
23252         this.startValue = this.getValue();
23253         
23254         if(this.groupId){
23255             Roo.bootstrap.CheckBox.register(this);
23256         }
23257     },
23258     
23259     onClick : function(e)
23260     {   
23261         if(this.fireEvent('click', this, e) !== false){
23262             this.setChecked(!this.checked);
23263         }
23264         
23265     },
23266     
23267     setChecked : function(state,suppressEvent)
23268     {
23269         this.startValue = this.getValue();
23270
23271         if(this.inputType == 'radio'){
23272             
23273             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23274                 e.dom.checked = false;
23275             });
23276             
23277             this.inputEl().dom.checked = true;
23278             
23279             this.inputEl().dom.value = this.inputValue;
23280             
23281             if(suppressEvent !== true){
23282                 this.fireEvent('check', this, true);
23283             }
23284             
23285             this.validate();
23286             
23287             return;
23288         }
23289         
23290         this.checked = state;
23291         
23292         this.inputEl().dom.checked = state;
23293         
23294         
23295         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23296         
23297         if(suppressEvent !== true){
23298             this.fireEvent('check', this, state);
23299         }
23300         
23301         this.validate();
23302     },
23303     
23304     getValue : function()
23305     {
23306         if(this.inputType == 'radio'){
23307             return this.getGroupValue();
23308         }
23309         
23310         return this.hiddenEl().dom.value;
23311         
23312     },
23313     
23314     getGroupValue : function()
23315     {
23316         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23317             return '';
23318         }
23319         
23320         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23321     },
23322     
23323     setValue : function(v,suppressEvent)
23324     {
23325         if(this.inputType == 'radio'){
23326             this.setGroupValue(v, suppressEvent);
23327             return;
23328         }
23329         
23330         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23331         
23332         this.validate();
23333     },
23334     
23335     setGroupValue : function(v, suppressEvent)
23336     {
23337         this.startValue = this.getValue();
23338         
23339         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23340             e.dom.checked = false;
23341             
23342             if(e.dom.value == v){
23343                 e.dom.checked = true;
23344             }
23345         });
23346         
23347         if(suppressEvent !== true){
23348             this.fireEvent('check', this, true);
23349         }
23350
23351         this.validate();
23352         
23353         return;
23354     },
23355     
23356     validate : function()
23357     {
23358         if(this.getVisibilityEl().hasClass('hidden')){
23359             return true;
23360         }
23361         
23362         if(
23363                 this.disabled || 
23364                 (this.inputType == 'radio' && this.validateRadio()) ||
23365                 (this.inputType == 'checkbox' && this.validateCheckbox())
23366         ){
23367             this.markValid();
23368             return true;
23369         }
23370         
23371         this.markInvalid();
23372         return false;
23373     },
23374     
23375     validateRadio : function()
23376     {
23377         if(this.getVisibilityEl().hasClass('hidden')){
23378             return true;
23379         }
23380         
23381         if(this.allowBlank){
23382             return true;
23383         }
23384         
23385         var valid = false;
23386         
23387         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23388             if(!e.dom.checked){
23389                 return;
23390             }
23391             
23392             valid = true;
23393             
23394             return false;
23395         });
23396         
23397         return valid;
23398     },
23399     
23400     validateCheckbox : function()
23401     {
23402         if(!this.groupId){
23403             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23404             //return (this.getValue() == this.inputValue) ? true : false;
23405         }
23406         
23407         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23408         
23409         if(!group){
23410             return false;
23411         }
23412         
23413         var r = false;
23414         
23415         for(var i in group){
23416             if(group[i].el.isVisible(true)){
23417                 r = false;
23418                 break;
23419             }
23420             
23421             r = true;
23422         }
23423         
23424         for(var i in group){
23425             if(r){
23426                 break;
23427             }
23428             
23429             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23430         }
23431         
23432         return r;
23433     },
23434     
23435     /**
23436      * Mark this field as valid
23437      */
23438     markValid : function()
23439     {
23440         var _this = this;
23441         
23442         this.fireEvent('valid', this);
23443         
23444         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23445         
23446         if(this.groupId){
23447             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23448         }
23449         
23450         if(label){
23451             label.markValid();
23452         }
23453
23454         if(this.inputType == 'radio'){
23455             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23456                 var fg = e.findParent('.form-group', false, true);
23457                 if (Roo.bootstrap.version == 3) {
23458                     fg.removeClass([_this.invalidClass, _this.validClass]);
23459                     fg.addClass(_this.validClass);
23460                 } else {
23461                     fg.removeClass(['is-valid', 'is-invalid']);
23462                     fg.addClass('is-valid');
23463                 }
23464             });
23465             
23466             return;
23467         }
23468
23469         if(!this.groupId){
23470             var fg = this.el.findParent('.form-group', false, true);
23471             if (Roo.bootstrap.version == 3) {
23472                 fg.removeClass([this.invalidClass, this.validClass]);
23473                 fg.addClass(this.validClass);
23474             } else {
23475                 fg.removeClass(['is-valid', 'is-invalid']);
23476                 fg.addClass('is-valid');
23477             }
23478             return;
23479         }
23480         
23481         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23482         
23483         if(!group){
23484             return;
23485         }
23486         
23487         for(var i in group){
23488             var fg = group[i].el.findParent('.form-group', false, true);
23489             if (Roo.bootstrap.version == 3) {
23490                 fg.removeClass([this.invalidClass, this.validClass]);
23491                 fg.addClass(this.validClass);
23492             } else {
23493                 fg.removeClass(['is-valid', 'is-invalid']);
23494                 fg.addClass('is-valid');
23495             }
23496         }
23497     },
23498     
23499      /**
23500      * Mark this field as invalid
23501      * @param {String} msg The validation message
23502      */
23503     markInvalid : function(msg)
23504     {
23505         if(this.allowBlank){
23506             return;
23507         }
23508         
23509         var _this = this;
23510         
23511         this.fireEvent('invalid', this, msg);
23512         
23513         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23514         
23515         if(this.groupId){
23516             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23517         }
23518         
23519         if(label){
23520             label.markInvalid();
23521         }
23522             
23523         if(this.inputType == 'radio'){
23524             
23525             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23526                 var fg = e.findParent('.form-group', false, true);
23527                 if (Roo.bootstrap.version == 3) {
23528                     fg.removeClass([_this.invalidClass, _this.validClass]);
23529                     fg.addClass(_this.invalidClass);
23530                 } else {
23531                     fg.removeClass(['is-invalid', 'is-valid']);
23532                     fg.addClass('is-invalid');
23533                 }
23534             });
23535             
23536             return;
23537         }
23538         
23539         if(!this.groupId){
23540             var fg = this.el.findParent('.form-group', false, true);
23541             if (Roo.bootstrap.version == 3) {
23542                 fg.removeClass([_this.invalidClass, _this.validClass]);
23543                 fg.addClass(_this.invalidClass);
23544             } else {
23545                 fg.removeClass(['is-invalid', 'is-valid']);
23546                 fg.addClass('is-invalid');
23547             }
23548             return;
23549         }
23550         
23551         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23552         
23553         if(!group){
23554             return;
23555         }
23556         
23557         for(var i in group){
23558             var fg = group[i].el.findParent('.form-group', false, true);
23559             if (Roo.bootstrap.version == 3) {
23560                 fg.removeClass([_this.invalidClass, _this.validClass]);
23561                 fg.addClass(_this.invalidClass);
23562             } else {
23563                 fg.removeClass(['is-invalid', 'is-valid']);
23564                 fg.addClass('is-invalid');
23565             }
23566         }
23567         
23568     },
23569     
23570     clearInvalid : function()
23571     {
23572         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23573         
23574         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23575         
23576         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23577         
23578         if (label && label.iconEl) {
23579             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23580             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23581         }
23582     },
23583     
23584     disable : function()
23585     {
23586         if(this.inputType != 'radio'){
23587             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23588             return;
23589         }
23590         
23591         var _this = this;
23592         
23593         if(this.rendered){
23594             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23595                 _this.getActionEl().addClass(this.disabledClass);
23596                 e.dom.disabled = true;
23597             });
23598         }
23599         
23600         this.disabled = true;
23601         this.fireEvent("disable", this);
23602         return this;
23603     },
23604
23605     enable : function()
23606     {
23607         if(this.inputType != 'radio'){
23608             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23609             return;
23610         }
23611         
23612         var _this = this;
23613         
23614         if(this.rendered){
23615             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23616                 _this.getActionEl().removeClass(this.disabledClass);
23617                 e.dom.disabled = false;
23618             });
23619         }
23620         
23621         this.disabled = false;
23622         this.fireEvent("enable", this);
23623         return this;
23624     },
23625     
23626     setBoxLabel : function(v)
23627     {
23628         this.boxLabel = v;
23629         
23630         if(this.rendered){
23631             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23632         }
23633     }
23634
23635 });
23636
23637 Roo.apply(Roo.bootstrap.CheckBox, {
23638     
23639     groups: {},
23640     
23641      /**
23642     * register a CheckBox Group
23643     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23644     */
23645     register : function(checkbox)
23646     {
23647         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23648             this.groups[checkbox.groupId] = {};
23649         }
23650         
23651         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23652             return;
23653         }
23654         
23655         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23656         
23657     },
23658     /**
23659     * fetch a CheckBox Group based on the group ID
23660     * @param {string} the group ID
23661     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23662     */
23663     get: function(groupId) {
23664         if (typeof(this.groups[groupId]) == 'undefined') {
23665             return false;
23666         }
23667         
23668         return this.groups[groupId] ;
23669     }
23670     
23671     
23672 });
23673 /*
23674  * - LGPL
23675  *
23676  * RadioItem
23677  * 
23678  */
23679
23680 /**
23681  * @class Roo.bootstrap.Radio
23682  * @extends Roo.bootstrap.Component
23683  * Bootstrap Radio class
23684  * @cfg {String} boxLabel - the label associated
23685  * @cfg {String} value - the value of radio
23686  * 
23687  * @constructor
23688  * Create a new Radio
23689  * @param {Object} config The config object
23690  */
23691 Roo.bootstrap.Radio = function(config){
23692     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23693     
23694 };
23695
23696 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23697     
23698     boxLabel : '',
23699     
23700     value : '',
23701     
23702     getAutoCreate : function()
23703     {
23704         var cfg = {
23705             tag : 'div',
23706             cls : 'form-group radio',
23707             cn : [
23708                 {
23709                     tag : 'label',
23710                     cls : 'box-label',
23711                     html : this.boxLabel
23712                 }
23713             ]
23714         };
23715         
23716         return cfg;
23717     },
23718     
23719     initEvents : function() 
23720     {
23721         this.parent().register(this);
23722         
23723         this.el.on('click', this.onClick, this);
23724         
23725     },
23726     
23727     onClick : function(e)
23728     {
23729         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23730             this.setChecked(true);
23731         }
23732     },
23733     
23734     setChecked : function(state, suppressEvent)
23735     {
23736         this.parent().setValue(this.value, suppressEvent);
23737         
23738     },
23739     
23740     setBoxLabel : function(v)
23741     {
23742         this.boxLabel = v;
23743         
23744         if(this.rendered){
23745             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23746         }
23747     }
23748     
23749 });
23750  
23751
23752  /*
23753  * - LGPL
23754  *
23755  * Input
23756  * 
23757  */
23758
23759 /**
23760  * @class Roo.bootstrap.SecurePass
23761  * @extends Roo.bootstrap.Input
23762  * Bootstrap SecurePass class
23763  *
23764  * 
23765  * @constructor
23766  * Create a new SecurePass
23767  * @param {Object} config The config object
23768  */
23769  
23770 Roo.bootstrap.SecurePass = function (config) {
23771     // these go here, so the translation tool can replace them..
23772     this.errors = {
23773         PwdEmpty: "Please type a password, and then retype it to confirm.",
23774         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23775         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23776         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23777         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23778         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23779         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23780         TooWeak: "Your password is Too Weak."
23781     },
23782     this.meterLabel = "Password strength:";
23783     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23784     this.meterClass = [
23785         "roo-password-meter-tooweak", 
23786         "roo-password-meter-weak", 
23787         "roo-password-meter-medium", 
23788         "roo-password-meter-strong", 
23789         "roo-password-meter-grey"
23790     ];
23791     
23792     this.errors = {};
23793     
23794     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23795 }
23796
23797 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23798     /**
23799      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23800      * {
23801      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23802      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23803      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23804      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23805      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23806      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23807      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23808      * })
23809      */
23810     // private
23811     
23812     meterWidth: 300,
23813     errorMsg :'',    
23814     errors: false,
23815     imageRoot: '/',
23816     /**
23817      * @cfg {String/Object} Label for the strength meter (defaults to
23818      * 'Password strength:')
23819      */
23820     // private
23821     meterLabel: '',
23822     /**
23823      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23824      * ['Weak', 'Medium', 'Strong'])
23825      */
23826     // private    
23827     pwdStrengths: false,    
23828     // private
23829     strength: 0,
23830     // private
23831     _lastPwd: null,
23832     // private
23833     kCapitalLetter: 0,
23834     kSmallLetter: 1,
23835     kDigit: 2,
23836     kPunctuation: 3,
23837     
23838     insecure: false,
23839     // private
23840     initEvents: function ()
23841     {
23842         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23843
23844         if (this.el.is('input[type=password]') && Roo.isSafari) {
23845             this.el.on('keydown', this.SafariOnKeyDown, this);
23846         }
23847
23848         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23849     },
23850     // private
23851     onRender: function (ct, position)
23852     {
23853         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23854         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23855         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23856
23857         this.trigger.createChild({
23858                    cn: [
23859                     {
23860                     //id: 'PwdMeter',
23861                     tag: 'div',
23862                     cls: 'roo-password-meter-grey col-xs-12',
23863                     style: {
23864                         //width: 0,
23865                         //width: this.meterWidth + 'px'                                                
23866                         }
23867                     },
23868                     {                            
23869                          cls: 'roo-password-meter-text'                          
23870                     }
23871                 ]            
23872         });
23873
23874          
23875         if (this.hideTrigger) {
23876             this.trigger.setDisplayed(false);
23877         }
23878         this.setSize(this.width || '', this.height || '');
23879     },
23880     // private
23881     onDestroy: function ()
23882     {
23883         if (this.trigger) {
23884             this.trigger.removeAllListeners();
23885             this.trigger.remove();
23886         }
23887         if (this.wrap) {
23888             this.wrap.remove();
23889         }
23890         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23891     },
23892     // private
23893     checkStrength: function ()
23894     {
23895         var pwd = this.inputEl().getValue();
23896         if (pwd == this._lastPwd) {
23897             return;
23898         }
23899
23900         var strength;
23901         if (this.ClientSideStrongPassword(pwd)) {
23902             strength = 3;
23903         } else if (this.ClientSideMediumPassword(pwd)) {
23904             strength = 2;
23905         } else if (this.ClientSideWeakPassword(pwd)) {
23906             strength = 1;
23907         } else {
23908             strength = 0;
23909         }
23910         
23911         Roo.log('strength1: ' + strength);
23912         
23913         //var pm = this.trigger.child('div/div/div').dom;
23914         var pm = this.trigger.child('div/div');
23915         pm.removeClass(this.meterClass);
23916         pm.addClass(this.meterClass[strength]);
23917                 
23918         
23919         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23920                 
23921         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23922         
23923         this._lastPwd = pwd;
23924     },
23925     reset: function ()
23926     {
23927         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23928         
23929         this._lastPwd = '';
23930         
23931         var pm = this.trigger.child('div/div');
23932         pm.removeClass(this.meterClass);
23933         pm.addClass('roo-password-meter-grey');        
23934         
23935         
23936         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23937         
23938         pt.innerHTML = '';
23939         this.inputEl().dom.type='password';
23940     },
23941     // private
23942     validateValue: function (value)
23943     {
23944         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23945             return false;
23946         }
23947         if (value.length == 0) {
23948             if (this.allowBlank) {
23949                 this.clearInvalid();
23950                 return true;
23951             }
23952
23953             this.markInvalid(this.errors.PwdEmpty);
23954             this.errorMsg = this.errors.PwdEmpty;
23955             return false;
23956         }
23957         
23958         if(this.insecure){
23959             return true;
23960         }
23961         
23962         if (!value.match(/[\x21-\x7e]+/)) {
23963             this.markInvalid(this.errors.PwdBadChar);
23964             this.errorMsg = this.errors.PwdBadChar;
23965             return false;
23966         }
23967         if (value.length < 6) {
23968             this.markInvalid(this.errors.PwdShort);
23969             this.errorMsg = this.errors.PwdShort;
23970             return false;
23971         }
23972         if (value.length > 16) {
23973             this.markInvalid(this.errors.PwdLong);
23974             this.errorMsg = this.errors.PwdLong;
23975             return false;
23976         }
23977         var strength;
23978         if (this.ClientSideStrongPassword(value)) {
23979             strength = 3;
23980         } else if (this.ClientSideMediumPassword(value)) {
23981             strength = 2;
23982         } else if (this.ClientSideWeakPassword(value)) {
23983             strength = 1;
23984         } else {
23985             strength = 0;
23986         }
23987
23988         
23989         if (strength < 2) {
23990             //this.markInvalid(this.errors.TooWeak);
23991             this.errorMsg = this.errors.TooWeak;
23992             //return false;
23993         }
23994         
23995         
23996         console.log('strength2: ' + strength);
23997         
23998         //var pm = this.trigger.child('div/div/div').dom;
23999         
24000         var pm = this.trigger.child('div/div');
24001         pm.removeClass(this.meterClass);
24002         pm.addClass(this.meterClass[strength]);
24003                 
24004         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24005                 
24006         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24007         
24008         this.errorMsg = ''; 
24009         return true;
24010     },
24011     // private
24012     CharacterSetChecks: function (type)
24013     {
24014         this.type = type;
24015         this.fResult = false;
24016     },
24017     // private
24018     isctype: function (character, type)
24019     {
24020         switch (type) {  
24021             case this.kCapitalLetter:
24022                 if (character >= 'A' && character <= 'Z') {
24023                     return true;
24024                 }
24025                 break;
24026             
24027             case this.kSmallLetter:
24028                 if (character >= 'a' && character <= 'z') {
24029                     return true;
24030                 }
24031                 break;
24032             
24033             case this.kDigit:
24034                 if (character >= '0' && character <= '9') {
24035                     return true;
24036                 }
24037                 break;
24038             
24039             case this.kPunctuation:
24040                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24041                     return true;
24042                 }
24043                 break;
24044             
24045             default:
24046                 return false;
24047         }
24048
24049     },
24050     // private
24051     IsLongEnough: function (pwd, size)
24052     {
24053         return !(pwd == null || isNaN(size) || pwd.length < size);
24054     },
24055     // private
24056     SpansEnoughCharacterSets: function (word, nb)
24057     {
24058         if (!this.IsLongEnough(word, nb))
24059         {
24060             return false;
24061         }
24062
24063         var characterSetChecks = new Array(
24064             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24065             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24066         );
24067         
24068         for (var index = 0; index < word.length; ++index) {
24069             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24070                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24071                     characterSetChecks[nCharSet].fResult = true;
24072                     break;
24073                 }
24074             }
24075         }
24076
24077         var nCharSets = 0;
24078         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24079             if (characterSetChecks[nCharSet].fResult) {
24080                 ++nCharSets;
24081             }
24082         }
24083
24084         if (nCharSets < nb) {
24085             return false;
24086         }
24087         return true;
24088     },
24089     // private
24090     ClientSideStrongPassword: function (pwd)
24091     {
24092         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24093     },
24094     // private
24095     ClientSideMediumPassword: function (pwd)
24096     {
24097         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24098     },
24099     // private
24100     ClientSideWeakPassword: function (pwd)
24101     {
24102         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24103     }
24104           
24105 })//<script type="text/javascript">
24106
24107 /*
24108  * Based  Ext JS Library 1.1.1
24109  * Copyright(c) 2006-2007, Ext JS, LLC.
24110  * LGPL
24111  *
24112  */
24113  
24114 /**
24115  * @class Roo.HtmlEditorCore
24116  * @extends Roo.Component
24117  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24118  *
24119  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24120  */
24121
24122 Roo.HtmlEditorCore = function(config){
24123     
24124     
24125     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24126     
24127     
24128     this.addEvents({
24129         /**
24130          * @event initialize
24131          * Fires when the editor is fully initialized (including the iframe)
24132          * @param {Roo.HtmlEditorCore} this
24133          */
24134         initialize: true,
24135         /**
24136          * @event activate
24137          * Fires when the editor is first receives the focus. Any insertion must wait
24138          * until after this event.
24139          * @param {Roo.HtmlEditorCore} this
24140          */
24141         activate: true,
24142          /**
24143          * @event beforesync
24144          * Fires before the textarea is updated with content from the editor iframe. Return false
24145          * to cancel the sync.
24146          * @param {Roo.HtmlEditorCore} this
24147          * @param {String} html
24148          */
24149         beforesync: true,
24150          /**
24151          * @event beforepush
24152          * Fires before the iframe editor is updated with content from the textarea. Return false
24153          * to cancel the push.
24154          * @param {Roo.HtmlEditorCore} this
24155          * @param {String} html
24156          */
24157         beforepush: true,
24158          /**
24159          * @event sync
24160          * Fires when the textarea is updated with content from the editor iframe.
24161          * @param {Roo.HtmlEditorCore} this
24162          * @param {String} html
24163          */
24164         sync: true,
24165          /**
24166          * @event push
24167          * Fires when the iframe editor is updated with content from the textarea.
24168          * @param {Roo.HtmlEditorCore} this
24169          * @param {String} html
24170          */
24171         push: true,
24172         
24173         /**
24174          * @event editorevent
24175          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24176          * @param {Roo.HtmlEditorCore} this
24177          */
24178         editorevent: true
24179         
24180     });
24181     
24182     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24183     
24184     // defaults : white / black...
24185     this.applyBlacklists();
24186     
24187     
24188     
24189 };
24190
24191
24192 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24193
24194
24195      /**
24196      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24197      */
24198     
24199     owner : false,
24200     
24201      /**
24202      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24203      *                        Roo.resizable.
24204      */
24205     resizable : false,
24206      /**
24207      * @cfg {Number} height (in pixels)
24208      */   
24209     height: 300,
24210    /**
24211      * @cfg {Number} width (in pixels)
24212      */   
24213     width: 500,
24214     
24215     /**
24216      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24217      * 
24218      */
24219     stylesheets: false,
24220     
24221     // id of frame..
24222     frameId: false,
24223     
24224     // private properties
24225     validationEvent : false,
24226     deferHeight: true,
24227     initialized : false,
24228     activated : false,
24229     sourceEditMode : false,
24230     onFocus : Roo.emptyFn,
24231     iframePad:3,
24232     hideMode:'offsets',
24233     
24234     clearUp: true,
24235     
24236     // blacklist + whitelisted elements..
24237     black: false,
24238     white: false,
24239      
24240     bodyCls : '',
24241
24242     /**
24243      * Protected method that will not generally be called directly. It
24244      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24245      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24246      */
24247     getDocMarkup : function(){
24248         // body styles..
24249         var st = '';
24250         
24251         // inherit styels from page...?? 
24252         if (this.stylesheets === false) {
24253             
24254             Roo.get(document.head).select('style').each(function(node) {
24255                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24256             });
24257             
24258             Roo.get(document.head).select('link').each(function(node) { 
24259                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24260             });
24261             
24262         } else if (!this.stylesheets.length) {
24263                 // simple..
24264                 st = '<style type="text/css">' +
24265                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24266                    '</style>';
24267         } else {
24268             for (var i in this.stylesheets) { 
24269                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24270             }
24271             
24272         }
24273         
24274         st +=  '<style type="text/css">' +
24275             'IMG { cursor: pointer } ' +
24276         '</style>';
24277
24278         var cls = 'roo-htmleditor-body';
24279         
24280         if(this.bodyCls.length){
24281             cls += ' ' + this.bodyCls;
24282         }
24283         
24284         return '<html><head>' + st  +
24285             //<style type="text/css">' +
24286             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24287             //'</style>' +
24288             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24289     },
24290
24291     // private
24292     onRender : function(ct, position)
24293     {
24294         var _t = this;
24295         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24296         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24297         
24298         
24299         this.el.dom.style.border = '0 none';
24300         this.el.dom.setAttribute('tabIndex', -1);
24301         this.el.addClass('x-hidden hide');
24302         
24303         
24304         
24305         if(Roo.isIE){ // fix IE 1px bogus margin
24306             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24307         }
24308        
24309         
24310         this.frameId = Roo.id();
24311         
24312          
24313         
24314         var iframe = this.owner.wrap.createChild({
24315             tag: 'iframe',
24316             cls: 'form-control', // bootstrap..
24317             id: this.frameId,
24318             name: this.frameId,
24319             frameBorder : 'no',
24320             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24321         }, this.el
24322         );
24323         
24324         
24325         this.iframe = iframe.dom;
24326
24327          this.assignDocWin();
24328         
24329         this.doc.designMode = 'on';
24330        
24331         this.doc.open();
24332         this.doc.write(this.getDocMarkup());
24333         this.doc.close();
24334
24335         
24336         var task = { // must defer to wait for browser to be ready
24337             run : function(){
24338                 //console.log("run task?" + this.doc.readyState);
24339                 this.assignDocWin();
24340                 if(this.doc.body || this.doc.readyState == 'complete'){
24341                     try {
24342                         this.doc.designMode="on";
24343                     } catch (e) {
24344                         return;
24345                     }
24346                     Roo.TaskMgr.stop(task);
24347                     this.initEditor.defer(10, this);
24348                 }
24349             },
24350             interval : 10,
24351             duration: 10000,
24352             scope: this
24353         };
24354         Roo.TaskMgr.start(task);
24355
24356     },
24357
24358     // private
24359     onResize : function(w, h)
24360     {
24361          Roo.log('resize: ' +w + ',' + h );
24362         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24363         if(!this.iframe){
24364             return;
24365         }
24366         if(typeof w == 'number'){
24367             
24368             this.iframe.style.width = w + 'px';
24369         }
24370         if(typeof h == 'number'){
24371             
24372             this.iframe.style.height = h + 'px';
24373             if(this.doc){
24374                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24375             }
24376         }
24377         
24378     },
24379
24380     /**
24381      * Toggles the editor between standard and source edit mode.
24382      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24383      */
24384     toggleSourceEdit : function(sourceEditMode){
24385         
24386         this.sourceEditMode = sourceEditMode === true;
24387         
24388         if(this.sourceEditMode){
24389  
24390             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24391             
24392         }else{
24393             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24394             //this.iframe.className = '';
24395             this.deferFocus();
24396         }
24397         //this.setSize(this.owner.wrap.getSize());
24398         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24399     },
24400
24401     
24402   
24403
24404     /**
24405      * Protected method that will not generally be called directly. If you need/want
24406      * custom HTML cleanup, this is the method you should override.
24407      * @param {String} html The HTML to be cleaned
24408      * return {String} The cleaned HTML
24409      */
24410     cleanHtml : function(html){
24411         html = String(html);
24412         if(html.length > 5){
24413             if(Roo.isSafari){ // strip safari nonsense
24414                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24415             }
24416         }
24417         if(html == '&nbsp;'){
24418             html = '';
24419         }
24420         return html;
24421     },
24422
24423     /**
24424      * HTML Editor -> Textarea
24425      * Protected method that will not generally be called directly. Syncs the contents
24426      * of the editor iframe with the textarea.
24427      */
24428     syncValue : function(){
24429         if(this.initialized){
24430             var bd = (this.doc.body || this.doc.documentElement);
24431             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24432             var html = bd.innerHTML;
24433             if(Roo.isSafari){
24434                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24435                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24436                 if(m && m[1]){
24437                     html = '<div style="'+m[0]+'">' + html + '</div>';
24438                 }
24439             }
24440             html = this.cleanHtml(html);
24441             // fix up the special chars.. normaly like back quotes in word...
24442             // however we do not want to do this with chinese..
24443             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24444                 
24445                 var cc = match.charCodeAt();
24446
24447                 // Get the character value, handling surrogate pairs
24448                 if (match.length == 2) {
24449                     // It's a surrogate pair, calculate the Unicode code point
24450                     var high = match.charCodeAt(0) - 0xD800;
24451                     var low  = match.charCodeAt(1) - 0xDC00;
24452                     cc = (high * 0x400) + low + 0x10000;
24453                 }  else if (
24454                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24455                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24456                     (cc >= 0xf900 && cc < 0xfb00 )
24457                 ) {
24458                         return match;
24459                 }  
24460          
24461                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24462                 return "&#" + cc + ";";
24463                 
24464                 
24465             });
24466             
24467             
24468              
24469             if(this.owner.fireEvent('beforesync', this, html) !== false){
24470                 this.el.dom.value = html;
24471                 this.owner.fireEvent('sync', this, html);
24472             }
24473         }
24474     },
24475
24476     /**
24477      * Protected method that will not generally be called directly. Pushes the value of the textarea
24478      * into the iframe editor.
24479      */
24480     pushValue : function(){
24481         if(this.initialized){
24482             var v = this.el.dom.value.trim();
24483             
24484 //            if(v.length < 1){
24485 //                v = '&#160;';
24486 //            }
24487             
24488             if(this.owner.fireEvent('beforepush', this, v) !== false){
24489                 var d = (this.doc.body || this.doc.documentElement);
24490                 d.innerHTML = v;
24491                 this.cleanUpPaste();
24492                 this.el.dom.value = d.innerHTML;
24493                 this.owner.fireEvent('push', this, v);
24494             }
24495         }
24496     },
24497
24498     // private
24499     deferFocus : function(){
24500         this.focus.defer(10, this);
24501     },
24502
24503     // doc'ed in Field
24504     focus : function(){
24505         if(this.win && !this.sourceEditMode){
24506             this.win.focus();
24507         }else{
24508             this.el.focus();
24509         }
24510     },
24511     
24512     assignDocWin: function()
24513     {
24514         var iframe = this.iframe;
24515         
24516          if(Roo.isIE){
24517             this.doc = iframe.contentWindow.document;
24518             this.win = iframe.contentWindow;
24519         } else {
24520 //            if (!Roo.get(this.frameId)) {
24521 //                return;
24522 //            }
24523 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24524 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24525             
24526             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24527                 return;
24528             }
24529             
24530             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24531             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24532         }
24533     },
24534     
24535     // private
24536     initEditor : function(){
24537         //console.log("INIT EDITOR");
24538         this.assignDocWin();
24539         
24540         
24541         
24542         this.doc.designMode="on";
24543         this.doc.open();
24544         this.doc.write(this.getDocMarkup());
24545         this.doc.close();
24546         
24547         var dbody = (this.doc.body || this.doc.documentElement);
24548         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24549         // this copies styles from the containing element into thsi one..
24550         // not sure why we need all of this..
24551         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24552         
24553         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24554         //ss['background-attachment'] = 'fixed'; // w3c
24555         dbody.bgProperties = 'fixed'; // ie
24556         //Roo.DomHelper.applyStyles(dbody, ss);
24557         Roo.EventManager.on(this.doc, {
24558             //'mousedown': this.onEditorEvent,
24559             'mouseup': this.onEditorEvent,
24560             'dblclick': this.onEditorEvent,
24561             'click': this.onEditorEvent,
24562             'keyup': this.onEditorEvent,
24563             buffer:100,
24564             scope: this
24565         });
24566         if(Roo.isGecko){
24567             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24568         }
24569         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24570             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24571         }
24572         this.initialized = true;
24573
24574         this.owner.fireEvent('initialize', this);
24575         this.pushValue();
24576     },
24577
24578     // private
24579     onDestroy : function(){
24580         
24581         
24582         
24583         if(this.rendered){
24584             
24585             //for (var i =0; i < this.toolbars.length;i++) {
24586             //    // fixme - ask toolbars for heights?
24587             //    this.toolbars[i].onDestroy();
24588            // }
24589             
24590             //this.wrap.dom.innerHTML = '';
24591             //this.wrap.remove();
24592         }
24593     },
24594
24595     // private
24596     onFirstFocus : function(){
24597         
24598         this.assignDocWin();
24599         
24600         
24601         this.activated = true;
24602          
24603     
24604         if(Roo.isGecko){ // prevent silly gecko errors
24605             this.win.focus();
24606             var s = this.win.getSelection();
24607             if(!s.focusNode || s.focusNode.nodeType != 3){
24608                 var r = s.getRangeAt(0);
24609                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24610                 r.collapse(true);
24611                 this.deferFocus();
24612             }
24613             try{
24614                 this.execCmd('useCSS', true);
24615                 this.execCmd('styleWithCSS', false);
24616             }catch(e){}
24617         }
24618         this.owner.fireEvent('activate', this);
24619     },
24620
24621     // private
24622     adjustFont: function(btn){
24623         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24624         //if(Roo.isSafari){ // safari
24625         //    adjust *= 2;
24626        // }
24627         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24628         if(Roo.isSafari){ // safari
24629             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24630             v =  (v < 10) ? 10 : v;
24631             v =  (v > 48) ? 48 : v;
24632             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24633             
24634         }
24635         
24636         
24637         v = Math.max(1, v+adjust);
24638         
24639         this.execCmd('FontSize', v  );
24640     },
24641
24642     onEditorEvent : function(e)
24643     {
24644         this.owner.fireEvent('editorevent', this, e);
24645       //  this.updateToolbar();
24646         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24647     },
24648
24649     insertTag : function(tg)
24650     {
24651         // could be a bit smarter... -> wrap the current selected tRoo..
24652         if (tg.toLowerCase() == 'span' ||
24653             tg.toLowerCase() == 'code' ||
24654             tg.toLowerCase() == 'sup' ||
24655             tg.toLowerCase() == 'sub' 
24656             ) {
24657             
24658             range = this.createRange(this.getSelection());
24659             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24660             wrappingNode.appendChild(range.extractContents());
24661             range.insertNode(wrappingNode);
24662
24663             return;
24664             
24665             
24666             
24667         }
24668         this.execCmd("formatblock",   tg);
24669         
24670     },
24671     
24672     insertText : function(txt)
24673     {
24674         
24675         
24676         var range = this.createRange();
24677         range.deleteContents();
24678                //alert(Sender.getAttribute('label'));
24679                
24680         range.insertNode(this.doc.createTextNode(txt));
24681     } ,
24682     
24683      
24684
24685     /**
24686      * Executes a Midas editor command on the editor document and performs necessary focus and
24687      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24688      * @param {String} cmd The Midas command
24689      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24690      */
24691     relayCmd : function(cmd, value){
24692         this.win.focus();
24693         this.execCmd(cmd, value);
24694         this.owner.fireEvent('editorevent', this);
24695         //this.updateToolbar();
24696         this.owner.deferFocus();
24697     },
24698
24699     /**
24700      * Executes a Midas editor command directly on the editor document.
24701      * For visual commands, you should use {@link #relayCmd} instead.
24702      * <b>This should only be called after the editor is initialized.</b>
24703      * @param {String} cmd The Midas command
24704      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24705      */
24706     execCmd : function(cmd, value){
24707         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24708         this.syncValue();
24709     },
24710  
24711  
24712    
24713     /**
24714      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24715      * to insert tRoo.
24716      * @param {String} text | dom node.. 
24717      */
24718     insertAtCursor : function(text)
24719     {
24720         
24721         if(!this.activated){
24722             return;
24723         }
24724         /*
24725         if(Roo.isIE){
24726             this.win.focus();
24727             var r = this.doc.selection.createRange();
24728             if(r){
24729                 r.collapse(true);
24730                 r.pasteHTML(text);
24731                 this.syncValue();
24732                 this.deferFocus();
24733             
24734             }
24735             return;
24736         }
24737         */
24738         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24739             this.win.focus();
24740             
24741             
24742             // from jquery ui (MIT licenced)
24743             var range, node;
24744             var win = this.win;
24745             
24746             if (win.getSelection && win.getSelection().getRangeAt) {
24747                 range = win.getSelection().getRangeAt(0);
24748                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24749                 range.insertNode(node);
24750             } else if (win.document.selection && win.document.selection.createRange) {
24751                 // no firefox support
24752                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24753                 win.document.selection.createRange().pasteHTML(txt);
24754             } else {
24755                 // no firefox support
24756                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24757                 this.execCmd('InsertHTML', txt);
24758             } 
24759             
24760             this.syncValue();
24761             
24762             this.deferFocus();
24763         }
24764     },
24765  // private
24766     mozKeyPress : function(e){
24767         if(e.ctrlKey){
24768             var c = e.getCharCode(), cmd;
24769           
24770             if(c > 0){
24771                 c = String.fromCharCode(c).toLowerCase();
24772                 switch(c){
24773                     case 'b':
24774                         cmd = 'bold';
24775                         break;
24776                     case 'i':
24777                         cmd = 'italic';
24778                         break;
24779                     
24780                     case 'u':
24781                         cmd = 'underline';
24782                         break;
24783                     
24784                     case 'v':
24785                         this.cleanUpPaste.defer(100, this);
24786                         return;
24787                         
24788                 }
24789                 if(cmd){
24790                     this.win.focus();
24791                     this.execCmd(cmd);
24792                     this.deferFocus();
24793                     e.preventDefault();
24794                 }
24795                 
24796             }
24797         }
24798     },
24799
24800     // private
24801     fixKeys : function(){ // load time branching for fastest keydown performance
24802         if(Roo.isIE){
24803             return function(e){
24804                 var k = e.getKey(), r;
24805                 if(k == e.TAB){
24806                     e.stopEvent();
24807                     r = this.doc.selection.createRange();
24808                     if(r){
24809                         r.collapse(true);
24810                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24811                         this.deferFocus();
24812                     }
24813                     return;
24814                 }
24815                 
24816                 if(k == e.ENTER){
24817                     r = this.doc.selection.createRange();
24818                     if(r){
24819                         var target = r.parentElement();
24820                         if(!target || target.tagName.toLowerCase() != 'li'){
24821                             e.stopEvent();
24822                             r.pasteHTML('<br />');
24823                             r.collapse(false);
24824                             r.select();
24825                         }
24826                     }
24827                 }
24828                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24829                     this.cleanUpPaste.defer(100, this);
24830                     return;
24831                 }
24832                 
24833                 
24834             };
24835         }else if(Roo.isOpera){
24836             return function(e){
24837                 var k = e.getKey();
24838                 if(k == e.TAB){
24839                     e.stopEvent();
24840                     this.win.focus();
24841                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24842                     this.deferFocus();
24843                 }
24844                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24845                     this.cleanUpPaste.defer(100, this);
24846                     return;
24847                 }
24848                 
24849             };
24850         }else if(Roo.isSafari){
24851             return function(e){
24852                 var k = e.getKey();
24853                 
24854                 if(k == e.TAB){
24855                     e.stopEvent();
24856                     this.execCmd('InsertText','\t');
24857                     this.deferFocus();
24858                     return;
24859                 }
24860                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24861                     this.cleanUpPaste.defer(100, this);
24862                     return;
24863                 }
24864                 
24865              };
24866         }
24867     }(),
24868     
24869     getAllAncestors: function()
24870     {
24871         var p = this.getSelectedNode();
24872         var a = [];
24873         if (!p) {
24874             a.push(p); // push blank onto stack..
24875             p = this.getParentElement();
24876         }
24877         
24878         
24879         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24880             a.push(p);
24881             p = p.parentNode;
24882         }
24883         a.push(this.doc.body);
24884         return a;
24885     },
24886     lastSel : false,
24887     lastSelNode : false,
24888     
24889     
24890     getSelection : function() 
24891     {
24892         this.assignDocWin();
24893         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24894     },
24895     
24896     getSelectedNode: function() 
24897     {
24898         // this may only work on Gecko!!!
24899         
24900         // should we cache this!!!!
24901         
24902         
24903         
24904          
24905         var range = this.createRange(this.getSelection()).cloneRange();
24906         
24907         if (Roo.isIE) {
24908             var parent = range.parentElement();
24909             while (true) {
24910                 var testRange = range.duplicate();
24911                 testRange.moveToElementText(parent);
24912                 if (testRange.inRange(range)) {
24913                     break;
24914                 }
24915                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24916                     break;
24917                 }
24918                 parent = parent.parentElement;
24919             }
24920             return parent;
24921         }
24922         
24923         // is ancestor a text element.
24924         var ac =  range.commonAncestorContainer;
24925         if (ac.nodeType == 3) {
24926             ac = ac.parentNode;
24927         }
24928         
24929         var ar = ac.childNodes;
24930          
24931         var nodes = [];
24932         var other_nodes = [];
24933         var has_other_nodes = false;
24934         for (var i=0;i<ar.length;i++) {
24935             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24936                 continue;
24937             }
24938             // fullly contained node.
24939             
24940             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24941                 nodes.push(ar[i]);
24942                 continue;
24943             }
24944             
24945             // probably selected..
24946             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24947                 other_nodes.push(ar[i]);
24948                 continue;
24949             }
24950             // outer..
24951             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24952                 continue;
24953             }
24954             
24955             
24956             has_other_nodes = true;
24957         }
24958         if (!nodes.length && other_nodes.length) {
24959             nodes= other_nodes;
24960         }
24961         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24962             return false;
24963         }
24964         
24965         return nodes[0];
24966     },
24967     createRange: function(sel)
24968     {
24969         // this has strange effects when using with 
24970         // top toolbar - not sure if it's a great idea.
24971         //this.editor.contentWindow.focus();
24972         if (typeof sel != "undefined") {
24973             try {
24974                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24975             } catch(e) {
24976                 return this.doc.createRange();
24977             }
24978         } else {
24979             return this.doc.createRange();
24980         }
24981     },
24982     getParentElement: function()
24983     {
24984         
24985         this.assignDocWin();
24986         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24987         
24988         var range = this.createRange(sel);
24989          
24990         try {
24991             var p = range.commonAncestorContainer;
24992             while (p.nodeType == 3) { // text node
24993                 p = p.parentNode;
24994             }
24995             return p;
24996         } catch (e) {
24997             return null;
24998         }
24999     
25000     },
25001     /***
25002      *
25003      * Range intersection.. the hard stuff...
25004      *  '-1' = before
25005      *  '0' = hits..
25006      *  '1' = after.
25007      *         [ -- selected range --- ]
25008      *   [fail]                        [fail]
25009      *
25010      *    basically..
25011      *      if end is before start or  hits it. fail.
25012      *      if start is after end or hits it fail.
25013      *
25014      *   if either hits (but other is outside. - then it's not 
25015      *   
25016      *    
25017      **/
25018     
25019     
25020     // @see http://www.thismuchiknow.co.uk/?p=64.
25021     rangeIntersectsNode : function(range, node)
25022     {
25023         var nodeRange = node.ownerDocument.createRange();
25024         try {
25025             nodeRange.selectNode(node);
25026         } catch (e) {
25027             nodeRange.selectNodeContents(node);
25028         }
25029     
25030         var rangeStartRange = range.cloneRange();
25031         rangeStartRange.collapse(true);
25032     
25033         var rangeEndRange = range.cloneRange();
25034         rangeEndRange.collapse(false);
25035     
25036         var nodeStartRange = nodeRange.cloneRange();
25037         nodeStartRange.collapse(true);
25038     
25039         var nodeEndRange = nodeRange.cloneRange();
25040         nodeEndRange.collapse(false);
25041     
25042         return rangeStartRange.compareBoundaryPoints(
25043                  Range.START_TO_START, nodeEndRange) == -1 &&
25044                rangeEndRange.compareBoundaryPoints(
25045                  Range.START_TO_START, nodeStartRange) == 1;
25046         
25047          
25048     },
25049     rangeCompareNode : function(range, node)
25050     {
25051         var nodeRange = node.ownerDocument.createRange();
25052         try {
25053             nodeRange.selectNode(node);
25054         } catch (e) {
25055             nodeRange.selectNodeContents(node);
25056         }
25057         
25058         
25059         range.collapse(true);
25060     
25061         nodeRange.collapse(true);
25062      
25063         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25064         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25065          
25066         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25067         
25068         var nodeIsBefore   =  ss == 1;
25069         var nodeIsAfter    = ee == -1;
25070         
25071         if (nodeIsBefore && nodeIsAfter) {
25072             return 0; // outer
25073         }
25074         if (!nodeIsBefore && nodeIsAfter) {
25075             return 1; //right trailed.
25076         }
25077         
25078         if (nodeIsBefore && !nodeIsAfter) {
25079             return 2;  // left trailed.
25080         }
25081         // fully contined.
25082         return 3;
25083     },
25084
25085     // private? - in a new class?
25086     cleanUpPaste :  function()
25087     {
25088         // cleans up the whole document..
25089         Roo.log('cleanuppaste');
25090         
25091         this.cleanUpChildren(this.doc.body);
25092         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25093         if (clean != this.doc.body.innerHTML) {
25094             this.doc.body.innerHTML = clean;
25095         }
25096         
25097     },
25098     
25099     cleanWordChars : function(input) {// change the chars to hex code
25100         var he = Roo.HtmlEditorCore;
25101         
25102         var output = input;
25103         Roo.each(he.swapCodes, function(sw) { 
25104             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25105             
25106             output = output.replace(swapper, sw[1]);
25107         });
25108         
25109         return output;
25110     },
25111     
25112     
25113     cleanUpChildren : function (n)
25114     {
25115         if (!n.childNodes.length) {
25116             return;
25117         }
25118         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25119            this.cleanUpChild(n.childNodes[i]);
25120         }
25121     },
25122     
25123     
25124         
25125     
25126     cleanUpChild : function (node)
25127     {
25128         var ed = this;
25129         //console.log(node);
25130         if (node.nodeName == "#text") {
25131             // clean up silly Windows -- stuff?
25132             return; 
25133         }
25134         if (node.nodeName == "#comment") {
25135             node.parentNode.removeChild(node);
25136             // clean up silly Windows -- stuff?
25137             return; 
25138         }
25139         var lcname = node.tagName.toLowerCase();
25140         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25141         // whitelist of tags..
25142         
25143         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25144             // remove node.
25145             node.parentNode.removeChild(node);
25146             return;
25147             
25148         }
25149         
25150         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25151         
25152         // spans with no attributes - just remove them..
25153         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
25154             remove_keep_children = true;
25155         }
25156         
25157         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25158         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25159         
25160         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25161         //    remove_keep_children = true;
25162         //}
25163         
25164         if (remove_keep_children) {
25165             this.cleanUpChildren(node);
25166             // inserts everything just before this node...
25167             while (node.childNodes.length) {
25168                 var cn = node.childNodes[0];
25169                 node.removeChild(cn);
25170                 node.parentNode.insertBefore(cn, node);
25171             }
25172             node.parentNode.removeChild(node);
25173             return;
25174         }
25175         
25176         if (!node.attributes || !node.attributes.length) {
25177             
25178           
25179             
25180             
25181             this.cleanUpChildren(node);
25182             return;
25183         }
25184         
25185         function cleanAttr(n,v)
25186         {
25187             
25188             if (v.match(/^\./) || v.match(/^\//)) {
25189                 return;
25190             }
25191             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25192                 return;
25193             }
25194             if (v.match(/^#/)) {
25195                 return;
25196             }
25197             if (v.match(/^\{/)) { // allow template editing.
25198                 return;
25199             }
25200 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25201             node.removeAttribute(n);
25202             
25203         }
25204         
25205         var cwhite = this.cwhite;
25206         var cblack = this.cblack;
25207             
25208         function cleanStyle(n,v)
25209         {
25210             if (v.match(/expression/)) { //XSS?? should we even bother..
25211                 node.removeAttribute(n);
25212                 return;
25213             }
25214             
25215             var parts = v.split(/;/);
25216             var clean = [];
25217             
25218             Roo.each(parts, function(p) {
25219                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25220                 if (!p.length) {
25221                     return true;
25222                 }
25223                 var l = p.split(':').shift().replace(/\s+/g,'');
25224                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25225                 
25226                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25227 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25228                     //node.removeAttribute(n);
25229                     return true;
25230                 }
25231                 //Roo.log()
25232                 // only allow 'c whitelisted system attributes'
25233                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25234 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25235                     //node.removeAttribute(n);
25236                     return true;
25237                 }
25238                 
25239                 
25240                  
25241                 
25242                 clean.push(p);
25243                 return true;
25244             });
25245             if (clean.length) { 
25246                 node.setAttribute(n, clean.join(';'));
25247             } else {
25248                 node.removeAttribute(n);
25249             }
25250             
25251         }
25252         
25253         
25254         for (var i = node.attributes.length-1; i > -1 ; i--) {
25255             var a = node.attributes[i];
25256             //console.log(a);
25257             
25258             if (a.name.toLowerCase().substr(0,2)=='on')  {
25259                 node.removeAttribute(a.name);
25260                 continue;
25261             }
25262             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25263                 node.removeAttribute(a.name);
25264                 continue;
25265             }
25266             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25267                 cleanAttr(a.name,a.value); // fixme..
25268                 continue;
25269             }
25270             if (a.name == 'style') {
25271                 cleanStyle(a.name,a.value);
25272                 continue;
25273             }
25274             /// clean up MS crap..
25275             // tecnically this should be a list of valid class'es..
25276             
25277             
25278             if (a.name == 'class') {
25279                 if (a.value.match(/^Mso/)) {
25280                     node.removeAttribute('class');
25281                 }
25282                 
25283                 if (a.value.match(/^body$/)) {
25284                     node.removeAttribute('class');
25285                 }
25286                 continue;
25287             }
25288             
25289             // style cleanup!?
25290             // class cleanup?
25291             
25292         }
25293         
25294         
25295         this.cleanUpChildren(node);
25296         
25297         
25298     },
25299     
25300     /**
25301      * Clean up MS wordisms...
25302      */
25303     cleanWord : function(node)
25304     {
25305         if (!node) {
25306             this.cleanWord(this.doc.body);
25307             return;
25308         }
25309         
25310         if(
25311                 node.nodeName == 'SPAN' &&
25312                 !node.hasAttributes() &&
25313                 node.childNodes.length == 1 &&
25314                 node.firstChild.nodeName == "#text"  
25315         ) {
25316             var textNode = node.firstChild;
25317             node.removeChild(textNode);
25318             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25319                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25320             }
25321             node.parentNode.insertBefore(textNode, node);
25322             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25323                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25324             }
25325             node.parentNode.removeChild(node);
25326         }
25327         
25328         if (node.nodeName == "#text") {
25329             // clean up silly Windows -- stuff?
25330             return; 
25331         }
25332         if (node.nodeName == "#comment") {
25333             node.parentNode.removeChild(node);
25334             // clean up silly Windows -- stuff?
25335             return; 
25336         }
25337         
25338         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25339             node.parentNode.removeChild(node);
25340             return;
25341         }
25342         //Roo.log(node.tagName);
25343         // remove - but keep children..
25344         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25345             //Roo.log('-- removed');
25346             while (node.childNodes.length) {
25347                 var cn = node.childNodes[0];
25348                 node.removeChild(cn);
25349                 node.parentNode.insertBefore(cn, node);
25350                 // move node to parent - and clean it..
25351                 this.cleanWord(cn);
25352             }
25353             node.parentNode.removeChild(node);
25354             /// no need to iterate chidlren = it's got none..
25355             //this.iterateChildren(node, this.cleanWord);
25356             return;
25357         }
25358         // clean styles
25359         if (node.className.length) {
25360             
25361             var cn = node.className.split(/\W+/);
25362             var cna = [];
25363             Roo.each(cn, function(cls) {
25364                 if (cls.match(/Mso[a-zA-Z]+/)) {
25365                     return;
25366                 }
25367                 cna.push(cls);
25368             });
25369             node.className = cna.length ? cna.join(' ') : '';
25370             if (!cna.length) {
25371                 node.removeAttribute("class");
25372             }
25373         }
25374         
25375         if (node.hasAttribute("lang")) {
25376             node.removeAttribute("lang");
25377         }
25378         
25379         if (node.hasAttribute("style")) {
25380             
25381             var styles = node.getAttribute("style").split(";");
25382             var nstyle = [];
25383             Roo.each(styles, function(s) {
25384                 if (!s.match(/:/)) {
25385                     return;
25386                 }
25387                 var kv = s.split(":");
25388                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25389                     return;
25390                 }
25391                 // what ever is left... we allow.
25392                 nstyle.push(s);
25393             });
25394             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25395             if (!nstyle.length) {
25396                 node.removeAttribute('style');
25397             }
25398         }
25399         this.iterateChildren(node, this.cleanWord);
25400         
25401         
25402         
25403     },
25404     /**
25405      * iterateChildren of a Node, calling fn each time, using this as the scole..
25406      * @param {DomNode} node node to iterate children of.
25407      * @param {Function} fn method of this class to call on each item.
25408      */
25409     iterateChildren : function(node, fn)
25410     {
25411         if (!node.childNodes.length) {
25412                 return;
25413         }
25414         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25415            fn.call(this, node.childNodes[i])
25416         }
25417     },
25418     
25419     
25420     /**
25421      * cleanTableWidths.
25422      *
25423      * Quite often pasting from word etc.. results in tables with column and widths.
25424      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25425      *
25426      */
25427     cleanTableWidths : function(node)
25428     {
25429          
25430          
25431         if (!node) {
25432             this.cleanTableWidths(this.doc.body);
25433             return;
25434         }
25435         
25436         // ignore list...
25437         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25438             return; 
25439         }
25440         Roo.log(node.tagName);
25441         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25442             this.iterateChildren(node, this.cleanTableWidths);
25443             return;
25444         }
25445         if (node.hasAttribute('width')) {
25446             node.removeAttribute('width');
25447         }
25448         
25449          
25450         if (node.hasAttribute("style")) {
25451             // pretty basic...
25452             
25453             var styles = node.getAttribute("style").split(";");
25454             var nstyle = [];
25455             Roo.each(styles, function(s) {
25456                 if (!s.match(/:/)) {
25457                     return;
25458                 }
25459                 var kv = s.split(":");
25460                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25461                     return;
25462                 }
25463                 // what ever is left... we allow.
25464                 nstyle.push(s);
25465             });
25466             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25467             if (!nstyle.length) {
25468                 node.removeAttribute('style');
25469             }
25470         }
25471         
25472         this.iterateChildren(node, this.cleanTableWidths);
25473         
25474         
25475     },
25476     
25477     
25478     
25479     
25480     domToHTML : function(currentElement, depth, nopadtext) {
25481         
25482         depth = depth || 0;
25483         nopadtext = nopadtext || false;
25484     
25485         if (!currentElement) {
25486             return this.domToHTML(this.doc.body);
25487         }
25488         
25489         //Roo.log(currentElement);
25490         var j;
25491         var allText = false;
25492         var nodeName = currentElement.nodeName;
25493         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25494         
25495         if  (nodeName == '#text') {
25496             
25497             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25498         }
25499         
25500         
25501         var ret = '';
25502         if (nodeName != 'BODY') {
25503              
25504             var i = 0;
25505             // Prints the node tagName, such as <A>, <IMG>, etc
25506             if (tagName) {
25507                 var attr = [];
25508                 for(i = 0; i < currentElement.attributes.length;i++) {
25509                     // quoting?
25510                     var aname = currentElement.attributes.item(i).name;
25511                     if (!currentElement.attributes.item(i).value.length) {
25512                         continue;
25513                     }
25514                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25515                 }
25516                 
25517                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25518             } 
25519             else {
25520                 
25521                 // eack
25522             }
25523         } else {
25524             tagName = false;
25525         }
25526         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25527             return ret;
25528         }
25529         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25530             nopadtext = true;
25531         }
25532         
25533         
25534         // Traverse the tree
25535         i = 0;
25536         var currentElementChild = currentElement.childNodes.item(i);
25537         var allText = true;
25538         var innerHTML  = '';
25539         lastnode = '';
25540         while (currentElementChild) {
25541             // Formatting code (indent the tree so it looks nice on the screen)
25542             var nopad = nopadtext;
25543             if (lastnode == 'SPAN') {
25544                 nopad  = true;
25545             }
25546             // text
25547             if  (currentElementChild.nodeName == '#text') {
25548                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25549                 toadd = nopadtext ? toadd : toadd.trim();
25550                 if (!nopad && toadd.length > 80) {
25551                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25552                 }
25553                 innerHTML  += toadd;
25554                 
25555                 i++;
25556                 currentElementChild = currentElement.childNodes.item(i);
25557                 lastNode = '';
25558                 continue;
25559             }
25560             allText = false;
25561             
25562             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25563                 
25564             // Recursively traverse the tree structure of the child node
25565             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25566             lastnode = currentElementChild.nodeName;
25567             i++;
25568             currentElementChild=currentElement.childNodes.item(i);
25569         }
25570         
25571         ret += innerHTML;
25572         
25573         if (!allText) {
25574                 // The remaining code is mostly for formatting the tree
25575             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25576         }
25577         
25578         
25579         if (tagName) {
25580             ret+= "</"+tagName+">";
25581         }
25582         return ret;
25583         
25584     },
25585         
25586     applyBlacklists : function()
25587     {
25588         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25589         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25590         
25591         this.white = [];
25592         this.black = [];
25593         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25594             if (b.indexOf(tag) > -1) {
25595                 return;
25596             }
25597             this.white.push(tag);
25598             
25599         }, this);
25600         
25601         Roo.each(w, function(tag) {
25602             if (b.indexOf(tag) > -1) {
25603                 return;
25604             }
25605             if (this.white.indexOf(tag) > -1) {
25606                 return;
25607             }
25608             this.white.push(tag);
25609             
25610         }, this);
25611         
25612         
25613         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25614             if (w.indexOf(tag) > -1) {
25615                 return;
25616             }
25617             this.black.push(tag);
25618             
25619         }, this);
25620         
25621         Roo.each(b, function(tag) {
25622             if (w.indexOf(tag) > -1) {
25623                 return;
25624             }
25625             if (this.black.indexOf(tag) > -1) {
25626                 return;
25627             }
25628             this.black.push(tag);
25629             
25630         }, this);
25631         
25632         
25633         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25634         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25635         
25636         this.cwhite = [];
25637         this.cblack = [];
25638         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25639             if (b.indexOf(tag) > -1) {
25640                 return;
25641             }
25642             this.cwhite.push(tag);
25643             
25644         }, this);
25645         
25646         Roo.each(w, function(tag) {
25647             if (b.indexOf(tag) > -1) {
25648                 return;
25649             }
25650             if (this.cwhite.indexOf(tag) > -1) {
25651                 return;
25652             }
25653             this.cwhite.push(tag);
25654             
25655         }, this);
25656         
25657         
25658         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25659             if (w.indexOf(tag) > -1) {
25660                 return;
25661             }
25662             this.cblack.push(tag);
25663             
25664         }, this);
25665         
25666         Roo.each(b, function(tag) {
25667             if (w.indexOf(tag) > -1) {
25668                 return;
25669             }
25670             if (this.cblack.indexOf(tag) > -1) {
25671                 return;
25672             }
25673             this.cblack.push(tag);
25674             
25675         }, this);
25676     },
25677     
25678     setStylesheets : function(stylesheets)
25679     {
25680         if(typeof(stylesheets) == 'string'){
25681             Roo.get(this.iframe.contentDocument.head).createChild({
25682                 tag : 'link',
25683                 rel : 'stylesheet',
25684                 type : 'text/css',
25685                 href : stylesheets
25686             });
25687             
25688             return;
25689         }
25690         var _this = this;
25691      
25692         Roo.each(stylesheets, function(s) {
25693             if(!s.length){
25694                 return;
25695             }
25696             
25697             Roo.get(_this.iframe.contentDocument.head).createChild({
25698                 tag : 'link',
25699                 rel : 'stylesheet',
25700                 type : 'text/css',
25701                 href : s
25702             });
25703         });
25704
25705         
25706     },
25707     
25708     removeStylesheets : function()
25709     {
25710         var _this = this;
25711         
25712         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25713             s.remove();
25714         });
25715     },
25716     
25717     setStyle : function(style)
25718     {
25719         Roo.get(this.iframe.contentDocument.head).createChild({
25720             tag : 'style',
25721             type : 'text/css',
25722             html : style
25723         });
25724
25725         return;
25726     }
25727     
25728     // hide stuff that is not compatible
25729     /**
25730      * @event blur
25731      * @hide
25732      */
25733     /**
25734      * @event change
25735      * @hide
25736      */
25737     /**
25738      * @event focus
25739      * @hide
25740      */
25741     /**
25742      * @event specialkey
25743      * @hide
25744      */
25745     /**
25746      * @cfg {String} fieldClass @hide
25747      */
25748     /**
25749      * @cfg {String} focusClass @hide
25750      */
25751     /**
25752      * @cfg {String} autoCreate @hide
25753      */
25754     /**
25755      * @cfg {String} inputType @hide
25756      */
25757     /**
25758      * @cfg {String} invalidClass @hide
25759      */
25760     /**
25761      * @cfg {String} invalidText @hide
25762      */
25763     /**
25764      * @cfg {String} msgFx @hide
25765      */
25766     /**
25767      * @cfg {String} validateOnBlur @hide
25768      */
25769 });
25770
25771 Roo.HtmlEditorCore.white = [
25772         'area', 'br', 'img', 'input', 'hr', 'wbr',
25773         
25774        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25775        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25776        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25777        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25778        'table',   'ul',         'xmp', 
25779        
25780        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25781       'thead',   'tr', 
25782      
25783       'dir', 'menu', 'ol', 'ul', 'dl',
25784        
25785       'embed',  'object'
25786 ];
25787
25788
25789 Roo.HtmlEditorCore.black = [
25790     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25791         'applet', // 
25792         'base',   'basefont', 'bgsound', 'blink',  'body', 
25793         'frame',  'frameset', 'head',    'html',   'ilayer', 
25794         'iframe', 'layer',  'link',     'meta',    'object',   
25795         'script', 'style' ,'title',  'xml' // clean later..
25796 ];
25797 Roo.HtmlEditorCore.clean = [
25798     'script', 'style', 'title', 'xml'
25799 ];
25800 Roo.HtmlEditorCore.remove = [
25801     'font'
25802 ];
25803 // attributes..
25804
25805 Roo.HtmlEditorCore.ablack = [
25806     'on'
25807 ];
25808     
25809 Roo.HtmlEditorCore.aclean = [ 
25810     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25811 ];
25812
25813 // protocols..
25814 Roo.HtmlEditorCore.pwhite= [
25815         'http',  'https',  'mailto'
25816 ];
25817
25818 // white listed style attributes.
25819 Roo.HtmlEditorCore.cwhite= [
25820       //  'text-align', /// default is to allow most things..
25821       
25822          
25823 //        'font-size'//??
25824 ];
25825
25826 // black listed style attributes.
25827 Roo.HtmlEditorCore.cblack= [
25828       //  'font-size' -- this can be set by the project 
25829 ];
25830
25831
25832 Roo.HtmlEditorCore.swapCodes   =[ 
25833     [    8211, "--" ], 
25834     [    8212, "--" ], 
25835     [    8216,  "'" ],  
25836     [    8217, "'" ],  
25837     [    8220, '"' ],  
25838     [    8221, '"' ],  
25839     [    8226, "*" ],  
25840     [    8230, "..." ]
25841 ]; 
25842
25843     /*
25844  * - LGPL
25845  *
25846  * HtmlEditor
25847  * 
25848  */
25849
25850 /**
25851  * @class Roo.bootstrap.HtmlEditor
25852  * @extends Roo.bootstrap.TextArea
25853  * Bootstrap HtmlEditor class
25854
25855  * @constructor
25856  * Create a new HtmlEditor
25857  * @param {Object} config The config object
25858  */
25859
25860 Roo.bootstrap.HtmlEditor = function(config){
25861     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25862     if (!this.toolbars) {
25863         this.toolbars = [];
25864     }
25865     
25866     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25867     this.addEvents({
25868             /**
25869              * @event initialize
25870              * Fires when the editor is fully initialized (including the iframe)
25871              * @param {HtmlEditor} this
25872              */
25873             initialize: true,
25874             /**
25875              * @event activate
25876              * Fires when the editor is first receives the focus. Any insertion must wait
25877              * until after this event.
25878              * @param {HtmlEditor} this
25879              */
25880             activate: true,
25881              /**
25882              * @event beforesync
25883              * Fires before the textarea is updated with content from the editor iframe. Return false
25884              * to cancel the sync.
25885              * @param {HtmlEditor} this
25886              * @param {String} html
25887              */
25888             beforesync: true,
25889              /**
25890              * @event beforepush
25891              * Fires before the iframe editor is updated with content from the textarea. Return false
25892              * to cancel the push.
25893              * @param {HtmlEditor} this
25894              * @param {String} html
25895              */
25896             beforepush: true,
25897              /**
25898              * @event sync
25899              * Fires when the textarea is updated with content from the editor iframe.
25900              * @param {HtmlEditor} this
25901              * @param {String} html
25902              */
25903             sync: true,
25904              /**
25905              * @event push
25906              * Fires when the iframe editor is updated with content from the textarea.
25907              * @param {HtmlEditor} this
25908              * @param {String} html
25909              */
25910             push: true,
25911              /**
25912              * @event editmodechange
25913              * Fires when the editor switches edit modes
25914              * @param {HtmlEditor} this
25915              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25916              */
25917             editmodechange: true,
25918             /**
25919              * @event editorevent
25920              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25921              * @param {HtmlEditor} this
25922              */
25923             editorevent: true,
25924             /**
25925              * @event firstfocus
25926              * Fires when on first focus - needed by toolbars..
25927              * @param {HtmlEditor} this
25928              */
25929             firstfocus: true,
25930             /**
25931              * @event autosave
25932              * Auto save the htmlEditor value as a file into Events
25933              * @param {HtmlEditor} this
25934              */
25935             autosave: true,
25936             /**
25937              * @event savedpreview
25938              * preview the saved version of htmlEditor
25939              * @param {HtmlEditor} this
25940              */
25941             savedpreview: true
25942         });
25943 };
25944
25945
25946 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25947     
25948     
25949       /**
25950      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25951      */
25952     toolbars : false,
25953     
25954      /**
25955     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25956     */
25957     btns : [],
25958    
25959      /**
25960      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25961      *                        Roo.resizable.
25962      */
25963     resizable : false,
25964      /**
25965      * @cfg {Number} height (in pixels)
25966      */   
25967     height: 300,
25968    /**
25969      * @cfg {Number} width (in pixels)
25970      */   
25971     width: false,
25972     
25973     /**
25974      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25975      * 
25976      */
25977     stylesheets: false,
25978     
25979     // id of frame..
25980     frameId: false,
25981     
25982     // private properties
25983     validationEvent : false,
25984     deferHeight: true,
25985     initialized : false,
25986     activated : false,
25987     
25988     onFocus : Roo.emptyFn,
25989     iframePad:3,
25990     hideMode:'offsets',
25991     
25992     tbContainer : false,
25993     
25994     bodyCls : '',
25995     
25996     toolbarContainer :function() {
25997         return this.wrap.select('.x-html-editor-tb',true).first();
25998     },
25999
26000     /**
26001      * Protected method that will not generally be called directly. It
26002      * is called when the editor creates its toolbar. Override this method if you need to
26003      * add custom toolbar buttons.
26004      * @param {HtmlEditor} editor
26005      */
26006     createToolbar : function(){
26007         Roo.log('renewing');
26008         Roo.log("create toolbars");
26009         
26010         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26011         this.toolbars[0].render(this.toolbarContainer());
26012         
26013         return;
26014         
26015 //        if (!editor.toolbars || !editor.toolbars.length) {
26016 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26017 //        }
26018 //        
26019 //        for (var i =0 ; i < editor.toolbars.length;i++) {
26020 //            editor.toolbars[i] = Roo.factory(
26021 //                    typeof(editor.toolbars[i]) == 'string' ?
26022 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
26023 //                Roo.bootstrap.HtmlEditor);
26024 //            editor.toolbars[i].init(editor);
26025 //        }
26026     },
26027
26028      
26029     // private
26030     onRender : function(ct, position)
26031     {
26032        // Roo.log("Call onRender: " + this.xtype);
26033         var _t = this;
26034         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26035       
26036         this.wrap = this.inputEl().wrap({
26037             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26038         });
26039         
26040         this.editorcore.onRender(ct, position);
26041          
26042         if (this.resizable) {
26043             this.resizeEl = new Roo.Resizable(this.wrap, {
26044                 pinned : true,
26045                 wrap: true,
26046                 dynamic : true,
26047                 minHeight : this.height,
26048                 height: this.height,
26049                 handles : this.resizable,
26050                 width: this.width,
26051                 listeners : {
26052                     resize : function(r, w, h) {
26053                         _t.onResize(w,h); // -something
26054                     }
26055                 }
26056             });
26057             
26058         }
26059         this.createToolbar(this);
26060        
26061         
26062         if(!this.width && this.resizable){
26063             this.setSize(this.wrap.getSize());
26064         }
26065         if (this.resizeEl) {
26066             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26067             // should trigger onReize..
26068         }
26069         
26070     },
26071
26072     // private
26073     onResize : function(w, h)
26074     {
26075         Roo.log('resize: ' +w + ',' + h );
26076         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26077         var ew = false;
26078         var eh = false;
26079         
26080         if(this.inputEl() ){
26081             if(typeof w == 'number'){
26082                 var aw = w - this.wrap.getFrameWidth('lr');
26083                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26084                 ew = aw;
26085             }
26086             if(typeof h == 'number'){
26087                  var tbh = -11;  // fixme it needs to tool bar size!
26088                 for (var i =0; i < this.toolbars.length;i++) {
26089                     // fixme - ask toolbars for heights?
26090                     tbh += this.toolbars[i].el.getHeight();
26091                     //if (this.toolbars[i].footer) {
26092                     //    tbh += this.toolbars[i].footer.el.getHeight();
26093                     //}
26094                 }
26095               
26096                 
26097                 
26098                 
26099                 
26100                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26101                 ah -= 5; // knock a few pixes off for look..
26102                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26103                 var eh = ah;
26104             }
26105         }
26106         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26107         this.editorcore.onResize(ew,eh);
26108         
26109     },
26110
26111     /**
26112      * Toggles the editor between standard and source edit mode.
26113      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26114      */
26115     toggleSourceEdit : function(sourceEditMode)
26116     {
26117         this.editorcore.toggleSourceEdit(sourceEditMode);
26118         
26119         if(this.editorcore.sourceEditMode){
26120             Roo.log('editor - showing textarea');
26121             
26122 //            Roo.log('in');
26123 //            Roo.log(this.syncValue());
26124             this.syncValue();
26125             this.inputEl().removeClass(['hide', 'x-hidden']);
26126             this.inputEl().dom.removeAttribute('tabIndex');
26127             this.inputEl().focus();
26128         }else{
26129             Roo.log('editor - hiding textarea');
26130 //            Roo.log('out')
26131 //            Roo.log(this.pushValue()); 
26132             this.pushValue();
26133             
26134             this.inputEl().addClass(['hide', 'x-hidden']);
26135             this.inputEl().dom.setAttribute('tabIndex', -1);
26136             //this.deferFocus();
26137         }
26138          
26139         if(this.resizable){
26140             this.setSize(this.wrap.getSize());
26141         }
26142         
26143         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26144     },
26145  
26146     // private (for BoxComponent)
26147     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26148
26149     // private (for BoxComponent)
26150     getResizeEl : function(){
26151         return this.wrap;
26152     },
26153
26154     // private (for BoxComponent)
26155     getPositionEl : function(){
26156         return this.wrap;
26157     },
26158
26159     // private
26160     initEvents : function(){
26161         this.originalValue = this.getValue();
26162     },
26163
26164 //    /**
26165 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26166 //     * @method
26167 //     */
26168 //    markInvalid : Roo.emptyFn,
26169 //    /**
26170 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26171 //     * @method
26172 //     */
26173 //    clearInvalid : Roo.emptyFn,
26174
26175     setValue : function(v){
26176         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26177         this.editorcore.pushValue();
26178     },
26179
26180      
26181     // private
26182     deferFocus : function(){
26183         this.focus.defer(10, this);
26184     },
26185
26186     // doc'ed in Field
26187     focus : function(){
26188         this.editorcore.focus();
26189         
26190     },
26191       
26192
26193     // private
26194     onDestroy : function(){
26195         
26196         
26197         
26198         if(this.rendered){
26199             
26200             for (var i =0; i < this.toolbars.length;i++) {
26201                 // fixme - ask toolbars for heights?
26202                 this.toolbars[i].onDestroy();
26203             }
26204             
26205             this.wrap.dom.innerHTML = '';
26206             this.wrap.remove();
26207         }
26208     },
26209
26210     // private
26211     onFirstFocus : function(){
26212         //Roo.log("onFirstFocus");
26213         this.editorcore.onFirstFocus();
26214          for (var i =0; i < this.toolbars.length;i++) {
26215             this.toolbars[i].onFirstFocus();
26216         }
26217         
26218     },
26219     
26220     // private
26221     syncValue : function()
26222     {   
26223         this.editorcore.syncValue();
26224     },
26225     
26226     pushValue : function()
26227     {   
26228         this.editorcore.pushValue();
26229     }
26230      
26231     
26232     // hide stuff that is not compatible
26233     /**
26234      * @event blur
26235      * @hide
26236      */
26237     /**
26238      * @event change
26239      * @hide
26240      */
26241     /**
26242      * @event focus
26243      * @hide
26244      */
26245     /**
26246      * @event specialkey
26247      * @hide
26248      */
26249     /**
26250      * @cfg {String} fieldClass @hide
26251      */
26252     /**
26253      * @cfg {String} focusClass @hide
26254      */
26255     /**
26256      * @cfg {String} autoCreate @hide
26257      */
26258     /**
26259      * @cfg {String} inputType @hide
26260      */
26261      
26262     /**
26263      * @cfg {String} invalidText @hide
26264      */
26265     /**
26266      * @cfg {String} msgFx @hide
26267      */
26268     /**
26269      * @cfg {String} validateOnBlur @hide
26270      */
26271 });
26272  
26273     
26274    
26275    
26276    
26277       
26278 Roo.namespace('Roo.bootstrap.htmleditor');
26279 /**
26280  * @class Roo.bootstrap.HtmlEditorToolbar1
26281  * Basic Toolbar
26282  * 
26283  * @example
26284  * Usage:
26285  *
26286  new Roo.bootstrap.HtmlEditor({
26287     ....
26288     toolbars : [
26289         new Roo.bootstrap.HtmlEditorToolbar1({
26290             disable : { fonts: 1 , format: 1, ..., ... , ...],
26291             btns : [ .... ]
26292         })
26293     }
26294      
26295  * 
26296  * @cfg {Object} disable List of elements to disable..
26297  * @cfg {Array} btns List of additional buttons.
26298  * 
26299  * 
26300  * NEEDS Extra CSS? 
26301  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26302  */
26303  
26304 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26305 {
26306     
26307     Roo.apply(this, config);
26308     
26309     // default disabled, based on 'good practice'..
26310     this.disable = this.disable || {};
26311     Roo.applyIf(this.disable, {
26312         fontSize : true,
26313         colors : true,
26314         specialElements : true
26315     });
26316     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26317     
26318     this.editor = config.editor;
26319     this.editorcore = config.editor.editorcore;
26320     
26321     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26322     
26323     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26324     // dont call parent... till later.
26325 }
26326 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26327      
26328     bar : true,
26329     
26330     editor : false,
26331     editorcore : false,
26332     
26333     
26334     formats : [
26335         "p" ,  
26336         "h1","h2","h3","h4","h5","h6", 
26337         "pre", "code", 
26338         "abbr", "acronym", "address", "cite", "samp", "var",
26339         'div','span'
26340     ],
26341     
26342     onRender : function(ct, position)
26343     {
26344        // Roo.log("Call onRender: " + this.xtype);
26345         
26346        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26347        Roo.log(this.el);
26348        this.el.dom.style.marginBottom = '0';
26349        var _this = this;
26350        var editorcore = this.editorcore;
26351        var editor= this.editor;
26352        
26353        var children = [];
26354        var btn = function(id,cmd , toggle, handler, html){
26355        
26356             var  event = toggle ? 'toggle' : 'click';
26357        
26358             var a = {
26359                 size : 'sm',
26360                 xtype: 'Button',
26361                 xns: Roo.bootstrap,
26362                 //glyphicon : id,
26363                 fa: id,
26364                 cmd : id || cmd,
26365                 enableToggle:toggle !== false,
26366                 html : html || '',
26367                 pressed : toggle ? false : null,
26368                 listeners : {}
26369             };
26370             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26371                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26372             };
26373             children.push(a);
26374             return a;
26375        }
26376        
26377     //    var cb_box = function...
26378         
26379         var style = {
26380                 xtype: 'Button',
26381                 size : 'sm',
26382                 xns: Roo.bootstrap,
26383                 fa : 'font',
26384                 //html : 'submit'
26385                 menu : {
26386                     xtype: 'Menu',
26387                     xns: Roo.bootstrap,
26388                     items:  []
26389                 }
26390         };
26391         Roo.each(this.formats, function(f) {
26392             style.menu.items.push({
26393                 xtype :'MenuItem',
26394                 xns: Roo.bootstrap,
26395                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26396                 tagname : f,
26397                 listeners : {
26398                     click : function()
26399                     {
26400                         editorcore.insertTag(this.tagname);
26401                         editor.focus();
26402                     }
26403                 }
26404                 
26405             });
26406         });
26407         children.push(style);   
26408         
26409         btn('bold',false,true);
26410         btn('italic',false,true);
26411         btn('align-left', 'justifyleft',true);
26412         btn('align-center', 'justifycenter',true);
26413         btn('align-right' , 'justifyright',true);
26414         btn('link', false, false, function(btn) {
26415             //Roo.log("create link?");
26416             var url = prompt(this.createLinkText, this.defaultLinkValue);
26417             if(url && url != 'http:/'+'/'){
26418                 this.editorcore.relayCmd('createlink', url);
26419             }
26420         }),
26421         btn('list','insertunorderedlist',true);
26422         btn('pencil', false,true, function(btn){
26423                 Roo.log(this);
26424                 this.toggleSourceEdit(btn.pressed);
26425         });
26426         
26427         if (this.editor.btns.length > 0) {
26428             for (var i = 0; i<this.editor.btns.length; i++) {
26429                 children.push(this.editor.btns[i]);
26430             }
26431         }
26432         
26433         /*
26434         var cog = {
26435                 xtype: 'Button',
26436                 size : 'sm',
26437                 xns: Roo.bootstrap,
26438                 glyphicon : 'cog',
26439                 //html : 'submit'
26440                 menu : {
26441                     xtype: 'Menu',
26442                     xns: Roo.bootstrap,
26443                     items:  []
26444                 }
26445         };
26446         
26447         cog.menu.items.push({
26448             xtype :'MenuItem',
26449             xns: Roo.bootstrap,
26450             html : Clean styles,
26451             tagname : f,
26452             listeners : {
26453                 click : function()
26454                 {
26455                     editorcore.insertTag(this.tagname);
26456                     editor.focus();
26457                 }
26458             }
26459             
26460         });
26461        */
26462         
26463          
26464        this.xtype = 'NavSimplebar';
26465         
26466         for(var i=0;i< children.length;i++) {
26467             
26468             this.buttons.add(this.addxtypeChild(children[i]));
26469             
26470         }
26471         
26472         editor.on('editorevent', this.updateToolbar, this);
26473     },
26474     onBtnClick : function(id)
26475     {
26476        this.editorcore.relayCmd(id);
26477        this.editorcore.focus();
26478     },
26479     
26480     /**
26481      * Protected method that will not generally be called directly. It triggers
26482      * a toolbar update by reading the markup state of the current selection in the editor.
26483      */
26484     updateToolbar: function(){
26485
26486         if(!this.editorcore.activated){
26487             this.editor.onFirstFocus(); // is this neeed?
26488             return;
26489         }
26490
26491         var btns = this.buttons; 
26492         var doc = this.editorcore.doc;
26493         btns.get('bold').setActive(doc.queryCommandState('bold'));
26494         btns.get('italic').setActive(doc.queryCommandState('italic'));
26495         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26496         
26497         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26498         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26499         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26500         
26501         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26502         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26503          /*
26504         
26505         var ans = this.editorcore.getAllAncestors();
26506         if (this.formatCombo) {
26507             
26508             
26509             var store = this.formatCombo.store;
26510             this.formatCombo.setValue("");
26511             for (var i =0; i < ans.length;i++) {
26512                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26513                     // select it..
26514                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26515                     break;
26516                 }
26517             }
26518         }
26519         
26520         
26521         
26522         // hides menus... - so this cant be on a menu...
26523         Roo.bootstrap.MenuMgr.hideAll();
26524         */
26525         Roo.bootstrap.MenuMgr.hideAll();
26526         //this.editorsyncValue();
26527     },
26528     onFirstFocus: function() {
26529         this.buttons.each(function(item){
26530            item.enable();
26531         });
26532     },
26533     toggleSourceEdit : function(sourceEditMode){
26534         
26535           
26536         if(sourceEditMode){
26537             Roo.log("disabling buttons");
26538            this.buttons.each( function(item){
26539                 if(item.cmd != 'pencil'){
26540                     item.disable();
26541                 }
26542             });
26543           
26544         }else{
26545             Roo.log("enabling buttons");
26546             if(this.editorcore.initialized){
26547                 this.buttons.each( function(item){
26548                     item.enable();
26549                 });
26550             }
26551             
26552         }
26553         Roo.log("calling toggole on editor");
26554         // tell the editor that it's been pressed..
26555         this.editor.toggleSourceEdit(sourceEditMode);
26556        
26557     }
26558 });
26559
26560
26561
26562
26563  
26564 /*
26565  * - LGPL
26566  */
26567
26568 /**
26569  * @class Roo.bootstrap.Markdown
26570  * @extends Roo.bootstrap.TextArea
26571  * Bootstrap Showdown editable area
26572  * @cfg {string} content
26573  * 
26574  * @constructor
26575  * Create a new Showdown
26576  */
26577
26578 Roo.bootstrap.Markdown = function(config){
26579     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26580    
26581 };
26582
26583 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26584     
26585     editing :false,
26586     
26587     initEvents : function()
26588     {
26589         
26590         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26591         this.markdownEl = this.el.createChild({
26592             cls : 'roo-markdown-area'
26593         });
26594         this.inputEl().addClass('d-none');
26595         if (this.getValue() == '') {
26596             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26597             
26598         } else {
26599             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26600         }
26601         this.markdownEl.on('click', this.toggleTextEdit, this);
26602         this.on('blur', this.toggleTextEdit, this);
26603         this.on('specialkey', this.resizeTextArea, this);
26604     },
26605     
26606     toggleTextEdit : function()
26607     {
26608         var sh = this.markdownEl.getHeight();
26609         this.inputEl().addClass('d-none');
26610         this.markdownEl.addClass('d-none');
26611         if (!this.editing) {
26612             // show editor?
26613             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26614             this.inputEl().removeClass('d-none');
26615             this.inputEl().focus();
26616             this.editing = true;
26617             return;
26618         }
26619         // show showdown...
26620         this.updateMarkdown();
26621         this.markdownEl.removeClass('d-none');
26622         this.editing = false;
26623         return;
26624     },
26625     updateMarkdown : function()
26626     {
26627         if (this.getValue() == '') {
26628             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26629             return;
26630         }
26631  
26632         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26633     },
26634     
26635     resizeTextArea: function () {
26636         
26637         var sh = 100;
26638         Roo.log([sh, this.getValue().split("\n").length * 30]);
26639         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26640     },
26641     setValue : function(val)
26642     {
26643         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26644         if (!this.editing) {
26645             this.updateMarkdown();
26646         }
26647         
26648     },
26649     focus : function()
26650     {
26651         if (!this.editing) {
26652             this.toggleTextEdit();
26653         }
26654         
26655     }
26656
26657
26658 });
26659 /**
26660  * @class Roo.bootstrap.Table.AbstractSelectionModel
26661  * @extends Roo.util.Observable
26662  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26663  * implemented by descendant classes.  This class should not be directly instantiated.
26664  * @constructor
26665  */
26666 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26667     this.locked = false;
26668     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26669 };
26670
26671
26672 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26673     /** @ignore Called by the grid automatically. Do not call directly. */
26674     init : function(grid){
26675         this.grid = grid;
26676         this.initEvents();
26677     },
26678
26679     /**
26680      * Locks the selections.
26681      */
26682     lock : function(){
26683         this.locked = true;
26684     },
26685
26686     /**
26687      * Unlocks the selections.
26688      */
26689     unlock : function(){
26690         this.locked = false;
26691     },
26692
26693     /**
26694      * Returns true if the selections are locked.
26695      * @return {Boolean}
26696      */
26697     isLocked : function(){
26698         return this.locked;
26699     },
26700     
26701     
26702     initEvents : function ()
26703     {
26704         
26705     }
26706 });
26707 /**
26708  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26709  * @class Roo.bootstrap.Table.RowSelectionModel
26710  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26711  * It supports multiple selections and keyboard selection/navigation. 
26712  * @constructor
26713  * @param {Object} config
26714  */
26715
26716 Roo.bootstrap.Table.RowSelectionModel = function(config){
26717     Roo.apply(this, config);
26718     this.selections = new Roo.util.MixedCollection(false, function(o){
26719         return o.id;
26720     });
26721
26722     this.last = false;
26723     this.lastActive = false;
26724
26725     this.addEvents({
26726         /**
26727              * @event selectionchange
26728              * Fires when the selection changes
26729              * @param {SelectionModel} this
26730              */
26731             "selectionchange" : true,
26732         /**
26733              * @event afterselectionchange
26734              * Fires after the selection changes (eg. by key press or clicking)
26735              * @param {SelectionModel} this
26736              */
26737             "afterselectionchange" : true,
26738         /**
26739              * @event beforerowselect
26740              * Fires when a row is selected being selected, return false to cancel.
26741              * @param {SelectionModel} this
26742              * @param {Number} rowIndex The selected index
26743              * @param {Boolean} keepExisting False if other selections will be cleared
26744              */
26745             "beforerowselect" : true,
26746         /**
26747              * @event rowselect
26748              * Fires when a row is selected.
26749              * @param {SelectionModel} this
26750              * @param {Number} rowIndex The selected index
26751              * @param {Roo.data.Record} r The record
26752              */
26753             "rowselect" : true,
26754         /**
26755              * @event rowdeselect
26756              * Fires when a row is deselected.
26757              * @param {SelectionModel} this
26758              * @param {Number} rowIndex The selected index
26759              */
26760         "rowdeselect" : true
26761     });
26762     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26763     this.locked = false;
26764  };
26765
26766 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26767     /**
26768      * @cfg {Boolean} singleSelect
26769      * True to allow selection of only one row at a time (defaults to false)
26770      */
26771     singleSelect : false,
26772
26773     // private
26774     initEvents : function()
26775     {
26776
26777         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26778         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26779         //}else{ // allow click to work like normal
26780          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26781         //}
26782         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26783         this.grid.on("rowclick", this.handleMouseDown, this);
26784         
26785         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26786             "up" : function(e){
26787                 if(!e.shiftKey){
26788                     this.selectPrevious(e.shiftKey);
26789                 }else if(this.last !== false && this.lastActive !== false){
26790                     var last = this.last;
26791                     this.selectRange(this.last,  this.lastActive-1);
26792                     this.grid.getView().focusRow(this.lastActive);
26793                     if(last !== false){
26794                         this.last = last;
26795                     }
26796                 }else{
26797                     this.selectFirstRow();
26798                 }
26799                 this.fireEvent("afterselectionchange", this);
26800             },
26801             "down" : function(e){
26802                 if(!e.shiftKey){
26803                     this.selectNext(e.shiftKey);
26804                 }else if(this.last !== false && this.lastActive !== false){
26805                     var last = this.last;
26806                     this.selectRange(this.last,  this.lastActive+1);
26807                     this.grid.getView().focusRow(this.lastActive);
26808                     if(last !== false){
26809                         this.last = last;
26810                     }
26811                 }else{
26812                     this.selectFirstRow();
26813                 }
26814                 this.fireEvent("afterselectionchange", this);
26815             },
26816             scope: this
26817         });
26818         this.grid.store.on('load', function(){
26819             this.selections.clear();
26820         },this);
26821         /*
26822         var view = this.grid.view;
26823         view.on("refresh", this.onRefresh, this);
26824         view.on("rowupdated", this.onRowUpdated, this);
26825         view.on("rowremoved", this.onRemove, this);
26826         */
26827     },
26828
26829     // private
26830     onRefresh : function()
26831     {
26832         var ds = this.grid.store, i, v = this.grid.view;
26833         var s = this.selections;
26834         s.each(function(r){
26835             if((i = ds.indexOfId(r.id)) != -1){
26836                 v.onRowSelect(i);
26837             }else{
26838                 s.remove(r);
26839             }
26840         });
26841     },
26842
26843     // private
26844     onRemove : function(v, index, r){
26845         this.selections.remove(r);
26846     },
26847
26848     // private
26849     onRowUpdated : function(v, index, r){
26850         if(this.isSelected(r)){
26851             v.onRowSelect(index);
26852         }
26853     },
26854
26855     /**
26856      * Select records.
26857      * @param {Array} records The records to select
26858      * @param {Boolean} keepExisting (optional) True to keep existing selections
26859      */
26860     selectRecords : function(records, keepExisting)
26861     {
26862         if(!keepExisting){
26863             this.clearSelections();
26864         }
26865             var ds = this.grid.store;
26866         for(var i = 0, len = records.length; i < len; i++){
26867             this.selectRow(ds.indexOf(records[i]), true);
26868         }
26869     },
26870
26871     /**
26872      * Gets the number of selected rows.
26873      * @return {Number}
26874      */
26875     getCount : function(){
26876         return this.selections.length;
26877     },
26878
26879     /**
26880      * Selects the first row in the grid.
26881      */
26882     selectFirstRow : function(){
26883         this.selectRow(0);
26884     },
26885
26886     /**
26887      * Select the last row.
26888      * @param {Boolean} keepExisting (optional) True to keep existing selections
26889      */
26890     selectLastRow : function(keepExisting){
26891         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26892         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26893     },
26894
26895     /**
26896      * Selects the row immediately following the last selected row.
26897      * @param {Boolean} keepExisting (optional) True to keep existing selections
26898      */
26899     selectNext : function(keepExisting)
26900     {
26901             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26902             this.selectRow(this.last+1, keepExisting);
26903             this.grid.getView().focusRow(this.last);
26904         }
26905     },
26906
26907     /**
26908      * Selects the row that precedes the last selected row.
26909      * @param {Boolean} keepExisting (optional) True to keep existing selections
26910      */
26911     selectPrevious : function(keepExisting){
26912         if(this.last){
26913             this.selectRow(this.last-1, keepExisting);
26914             this.grid.getView().focusRow(this.last);
26915         }
26916     },
26917
26918     /**
26919      * Returns the selected records
26920      * @return {Array} Array of selected records
26921      */
26922     getSelections : function(){
26923         return [].concat(this.selections.items);
26924     },
26925
26926     /**
26927      * Returns the first selected record.
26928      * @return {Record}
26929      */
26930     getSelected : function(){
26931         return this.selections.itemAt(0);
26932     },
26933
26934
26935     /**
26936      * Clears all selections.
26937      */
26938     clearSelections : function(fast)
26939     {
26940         if(this.locked) {
26941             return;
26942         }
26943         if(fast !== true){
26944                 var ds = this.grid.store;
26945             var s = this.selections;
26946             s.each(function(r){
26947                 this.deselectRow(ds.indexOfId(r.id));
26948             }, this);
26949             s.clear();
26950         }else{
26951             this.selections.clear();
26952         }
26953         this.last = false;
26954     },
26955
26956
26957     /**
26958      * Selects all rows.
26959      */
26960     selectAll : function(){
26961         if(this.locked) {
26962             return;
26963         }
26964         this.selections.clear();
26965         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26966             this.selectRow(i, true);
26967         }
26968     },
26969
26970     /**
26971      * Returns True if there is a selection.
26972      * @return {Boolean}
26973      */
26974     hasSelection : function(){
26975         return this.selections.length > 0;
26976     },
26977
26978     /**
26979      * Returns True if the specified row is selected.
26980      * @param {Number/Record} record The record or index of the record to check
26981      * @return {Boolean}
26982      */
26983     isSelected : function(index){
26984             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26985         return (r && this.selections.key(r.id) ? true : false);
26986     },
26987
26988     /**
26989      * Returns True if the specified record id is selected.
26990      * @param {String} id The id of record to check
26991      * @return {Boolean}
26992      */
26993     isIdSelected : function(id){
26994         return (this.selections.key(id) ? true : false);
26995     },
26996
26997
26998     // private
26999     handleMouseDBClick : function(e, t){
27000         
27001     },
27002     // private
27003     handleMouseDown : function(e, t)
27004     {
27005             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27006         if(this.isLocked() || rowIndex < 0 ){
27007             return;
27008         };
27009         if(e.shiftKey && this.last !== false){
27010             var last = this.last;
27011             this.selectRange(last, rowIndex, e.ctrlKey);
27012             this.last = last; // reset the last
27013             t.focus();
27014     
27015         }else{
27016             var isSelected = this.isSelected(rowIndex);
27017             //Roo.log("select row:" + rowIndex);
27018             if(isSelected){
27019                 this.deselectRow(rowIndex);
27020             } else {
27021                         this.selectRow(rowIndex, true);
27022             }
27023     
27024             /*
27025                 if(e.button !== 0 && isSelected){
27026                 alert('rowIndex 2: ' + rowIndex);
27027                     view.focusRow(rowIndex);
27028                 }else if(e.ctrlKey && isSelected){
27029                     this.deselectRow(rowIndex);
27030                 }else if(!isSelected){
27031                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27032                     view.focusRow(rowIndex);
27033                 }
27034             */
27035         }
27036         this.fireEvent("afterselectionchange", this);
27037     },
27038     // private
27039     handleDragableRowClick :  function(grid, rowIndex, e) 
27040     {
27041         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27042             this.selectRow(rowIndex, false);
27043             grid.view.focusRow(rowIndex);
27044              this.fireEvent("afterselectionchange", this);
27045         }
27046     },
27047     
27048     /**
27049      * Selects multiple rows.
27050      * @param {Array} rows Array of the indexes of the row to select
27051      * @param {Boolean} keepExisting (optional) True to keep existing selections
27052      */
27053     selectRows : function(rows, keepExisting){
27054         if(!keepExisting){
27055             this.clearSelections();
27056         }
27057         for(var i = 0, len = rows.length; i < len; i++){
27058             this.selectRow(rows[i], true);
27059         }
27060     },
27061
27062     /**
27063      * Selects a range of rows. All rows in between startRow and endRow are also selected.
27064      * @param {Number} startRow The index of the first row in the range
27065      * @param {Number} endRow The index of the last row in the range
27066      * @param {Boolean} keepExisting (optional) True to retain existing selections
27067      */
27068     selectRange : function(startRow, endRow, keepExisting){
27069         if(this.locked) {
27070             return;
27071         }
27072         if(!keepExisting){
27073             this.clearSelections();
27074         }
27075         if(startRow <= endRow){
27076             for(var i = startRow; i <= endRow; i++){
27077                 this.selectRow(i, true);
27078             }
27079         }else{
27080             for(var i = startRow; i >= endRow; i--){
27081                 this.selectRow(i, true);
27082             }
27083         }
27084     },
27085
27086     /**
27087      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27088      * @param {Number} startRow The index of the first row in the range
27089      * @param {Number} endRow The index of the last row in the range
27090      */
27091     deselectRange : function(startRow, endRow, preventViewNotify){
27092         if(this.locked) {
27093             return;
27094         }
27095         for(var i = startRow; i <= endRow; i++){
27096             this.deselectRow(i, preventViewNotify);
27097         }
27098     },
27099
27100     /**
27101      * Selects a row.
27102      * @param {Number} row The index of the row to select
27103      * @param {Boolean} keepExisting (optional) True to keep existing selections
27104      */
27105     selectRow : function(index, keepExisting, preventViewNotify)
27106     {
27107             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27108             return;
27109         }
27110         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27111             if(!keepExisting || this.singleSelect){
27112                 this.clearSelections();
27113             }
27114             
27115             var r = this.grid.store.getAt(index);
27116             //console.log('selectRow - record id :' + r.id);
27117             
27118             this.selections.add(r);
27119             this.last = this.lastActive = index;
27120             if(!preventViewNotify){
27121                 var proxy = new Roo.Element(
27122                                 this.grid.getRowDom(index)
27123                 );
27124                 proxy.addClass('bg-info info');
27125             }
27126             this.fireEvent("rowselect", this, index, r);
27127             this.fireEvent("selectionchange", this);
27128         }
27129     },
27130
27131     /**
27132      * Deselects a row.
27133      * @param {Number} row The index of the row to deselect
27134      */
27135     deselectRow : function(index, preventViewNotify)
27136     {
27137         if(this.locked) {
27138             return;
27139         }
27140         if(this.last == index){
27141             this.last = false;
27142         }
27143         if(this.lastActive == index){
27144             this.lastActive = false;
27145         }
27146         
27147         var r = this.grid.store.getAt(index);
27148         if (!r) {
27149             return;
27150         }
27151         
27152         this.selections.remove(r);
27153         //.console.log('deselectRow - record id :' + r.id);
27154         if(!preventViewNotify){
27155         
27156             var proxy = new Roo.Element(
27157                 this.grid.getRowDom(index)
27158             );
27159             proxy.removeClass('bg-info info');
27160         }
27161         this.fireEvent("rowdeselect", this, index);
27162         this.fireEvent("selectionchange", this);
27163     },
27164
27165     // private
27166     restoreLast : function(){
27167         if(this._last){
27168             this.last = this._last;
27169         }
27170     },
27171
27172     // private
27173     acceptsNav : function(row, col, cm){
27174         return !cm.isHidden(col) && cm.isCellEditable(col, row);
27175     },
27176
27177     // private
27178     onEditorKey : function(field, e){
27179         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27180         if(k == e.TAB){
27181             e.stopEvent();
27182             ed.completeEdit();
27183             if(e.shiftKey){
27184                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27185             }else{
27186                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27187             }
27188         }else if(k == e.ENTER && !e.ctrlKey){
27189             e.stopEvent();
27190             ed.completeEdit();
27191             if(e.shiftKey){
27192                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27193             }else{
27194                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27195             }
27196         }else if(k == e.ESC){
27197             ed.cancelEdit();
27198         }
27199         if(newCell){
27200             g.startEditing(newCell[0], newCell[1]);
27201         }
27202     }
27203 });
27204 /*
27205  * Based on:
27206  * Ext JS Library 1.1.1
27207  * Copyright(c) 2006-2007, Ext JS, LLC.
27208  *
27209  * Originally Released Under LGPL - original licence link has changed is not relivant.
27210  *
27211  * Fork - LGPL
27212  * <script type="text/javascript">
27213  */
27214  
27215 /**
27216  * @class Roo.bootstrap.PagingToolbar
27217  * @extends Roo.bootstrap.NavSimplebar
27218  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27219  * @constructor
27220  * Create a new PagingToolbar
27221  * @param {Object} config The config object
27222  * @param {Roo.data.Store} store
27223  */
27224 Roo.bootstrap.PagingToolbar = function(config)
27225 {
27226     // old args format still supported... - xtype is prefered..
27227         // created from xtype...
27228     
27229     this.ds = config.dataSource;
27230     
27231     if (config.store && !this.ds) {
27232         this.store= Roo.factory(config.store, Roo.data);
27233         this.ds = this.store;
27234         this.ds.xmodule = this.xmodule || false;
27235     }
27236     
27237     this.toolbarItems = [];
27238     if (config.items) {
27239         this.toolbarItems = config.items;
27240     }
27241     
27242     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27243     
27244     this.cursor = 0;
27245     
27246     if (this.ds) { 
27247         this.bind(this.ds);
27248     }
27249     
27250     if (Roo.bootstrap.version == 4) {
27251         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27252     } else {
27253         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27254     }
27255     
27256 };
27257
27258 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27259     /**
27260      * @cfg {Roo.data.Store} dataSource
27261      * The underlying data store providing the paged data
27262      */
27263     /**
27264      * @cfg {String/HTMLElement/Element} container
27265      * container The id or element that will contain the toolbar
27266      */
27267     /**
27268      * @cfg {Boolean} displayInfo
27269      * True to display the displayMsg (defaults to false)
27270      */
27271     /**
27272      * @cfg {Number} pageSize
27273      * The number of records to display per page (defaults to 20)
27274      */
27275     pageSize: 20,
27276     /**
27277      * @cfg {String} displayMsg
27278      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27279      */
27280     displayMsg : 'Displaying {0} - {1} of {2}',
27281     /**
27282      * @cfg {String} emptyMsg
27283      * The message to display when no records are found (defaults to "No data to display")
27284      */
27285     emptyMsg : 'No data to display',
27286     /**
27287      * Customizable piece of the default paging text (defaults to "Page")
27288      * @type String
27289      */
27290     beforePageText : "Page",
27291     /**
27292      * Customizable piece of the default paging text (defaults to "of %0")
27293      * @type String
27294      */
27295     afterPageText : "of {0}",
27296     /**
27297      * Customizable piece of the default paging text (defaults to "First Page")
27298      * @type String
27299      */
27300     firstText : "First Page",
27301     /**
27302      * Customizable piece of the default paging text (defaults to "Previous Page")
27303      * @type String
27304      */
27305     prevText : "Previous Page",
27306     /**
27307      * Customizable piece of the default paging text (defaults to "Next Page")
27308      * @type String
27309      */
27310     nextText : "Next Page",
27311     /**
27312      * Customizable piece of the default paging text (defaults to "Last Page")
27313      * @type String
27314      */
27315     lastText : "Last Page",
27316     /**
27317      * Customizable piece of the default paging text (defaults to "Refresh")
27318      * @type String
27319      */
27320     refreshText : "Refresh",
27321
27322     buttons : false,
27323     // private
27324     onRender : function(ct, position) 
27325     {
27326         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27327         this.navgroup.parentId = this.id;
27328         this.navgroup.onRender(this.el, null);
27329         // add the buttons to the navgroup
27330         
27331         if(this.displayInfo){
27332             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27333             this.displayEl = this.el.select('.x-paging-info', true).first();
27334 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27335 //            this.displayEl = navel.el.select('span',true).first();
27336         }
27337         
27338         var _this = this;
27339         
27340         if(this.buttons){
27341             Roo.each(_this.buttons, function(e){ // this might need to use render????
27342                Roo.factory(e).render(_this.el);
27343             });
27344         }
27345             
27346         Roo.each(_this.toolbarItems, function(e) {
27347             _this.navgroup.addItem(e);
27348         });
27349         
27350         
27351         this.first = this.navgroup.addItem({
27352             tooltip: this.firstText,
27353             cls: "prev btn-outline-secondary",
27354             html : ' <i class="fa fa-step-backward"></i>',
27355             disabled: true,
27356             preventDefault: true,
27357             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27358         });
27359         
27360         this.prev =  this.navgroup.addItem({
27361             tooltip: this.prevText,
27362             cls: "prev btn-outline-secondary",
27363             html : ' <i class="fa fa-backward"></i>',
27364             disabled: true,
27365             preventDefault: true,
27366             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27367         });
27368     //this.addSeparator();
27369         
27370         
27371         var field = this.navgroup.addItem( {
27372             tagtype : 'span',
27373             cls : 'x-paging-position  btn-outline-secondary',
27374              disabled: true,
27375             html : this.beforePageText  +
27376                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27377                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27378          } ); //?? escaped?
27379         
27380         this.field = field.el.select('input', true).first();
27381         this.field.on("keydown", this.onPagingKeydown, this);
27382         this.field.on("focus", function(){this.dom.select();});
27383     
27384     
27385         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27386         //this.field.setHeight(18);
27387         //this.addSeparator();
27388         this.next = this.navgroup.addItem({
27389             tooltip: this.nextText,
27390             cls: "next btn-outline-secondary",
27391             html : ' <i class="fa fa-forward"></i>',
27392             disabled: true,
27393             preventDefault: true,
27394             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27395         });
27396         this.last = this.navgroup.addItem({
27397             tooltip: this.lastText,
27398             html : ' <i class="fa fa-step-forward"></i>',
27399             cls: "next btn-outline-secondary",
27400             disabled: true,
27401             preventDefault: true,
27402             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27403         });
27404     //this.addSeparator();
27405         this.loading = this.navgroup.addItem({
27406             tooltip: this.refreshText,
27407             cls: "btn-outline-secondary",
27408             html : ' <i class="fa fa-refresh"></i>',
27409             preventDefault: true,
27410             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27411         });
27412         
27413     },
27414
27415     // private
27416     updateInfo : function(){
27417         if(this.displayEl){
27418             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27419             var msg = count == 0 ?
27420                 this.emptyMsg :
27421                 String.format(
27422                     this.displayMsg,
27423                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27424                 );
27425             this.displayEl.update(msg);
27426         }
27427     },
27428
27429     // private
27430     onLoad : function(ds, r, o)
27431     {
27432         this.cursor = o.params && o.params.start ? o.params.start : 0;
27433         
27434         var d = this.getPageData(),
27435             ap = d.activePage,
27436             ps = d.pages;
27437         
27438         
27439         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27440         this.field.dom.value = ap;
27441         this.first.setDisabled(ap == 1);
27442         this.prev.setDisabled(ap == 1);
27443         this.next.setDisabled(ap == ps);
27444         this.last.setDisabled(ap == ps);
27445         this.loading.enable();
27446         this.updateInfo();
27447     },
27448
27449     // private
27450     getPageData : function(){
27451         var total = this.ds.getTotalCount();
27452         return {
27453             total : total,
27454             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27455             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27456         };
27457     },
27458
27459     // private
27460     onLoadError : function(){
27461         this.loading.enable();
27462     },
27463
27464     // private
27465     onPagingKeydown : function(e){
27466         var k = e.getKey();
27467         var d = this.getPageData();
27468         if(k == e.RETURN){
27469             var v = this.field.dom.value, pageNum;
27470             if(!v || isNaN(pageNum = parseInt(v, 10))){
27471                 this.field.dom.value = d.activePage;
27472                 return;
27473             }
27474             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27475             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27476             e.stopEvent();
27477         }
27478         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))
27479         {
27480           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27481           this.field.dom.value = pageNum;
27482           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27483           e.stopEvent();
27484         }
27485         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27486         {
27487           var v = this.field.dom.value, pageNum; 
27488           var increment = (e.shiftKey) ? 10 : 1;
27489           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27490                 increment *= -1;
27491           }
27492           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27493             this.field.dom.value = d.activePage;
27494             return;
27495           }
27496           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27497           {
27498             this.field.dom.value = parseInt(v, 10) + increment;
27499             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27500             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27501           }
27502           e.stopEvent();
27503         }
27504     },
27505
27506     // private
27507     beforeLoad : function(){
27508         if(this.loading){
27509             this.loading.disable();
27510         }
27511     },
27512
27513     // private
27514     onClick : function(which){
27515         
27516         var ds = this.ds;
27517         if (!ds) {
27518             return;
27519         }
27520         
27521         switch(which){
27522             case "first":
27523                 ds.load({params:{start: 0, limit: this.pageSize}});
27524             break;
27525             case "prev":
27526                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27527             break;
27528             case "next":
27529                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27530             break;
27531             case "last":
27532                 var total = ds.getTotalCount();
27533                 var extra = total % this.pageSize;
27534                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27535                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27536             break;
27537             case "refresh":
27538                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27539             break;
27540         }
27541     },
27542
27543     /**
27544      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27545      * @param {Roo.data.Store} store The data store to unbind
27546      */
27547     unbind : function(ds){
27548         ds.un("beforeload", this.beforeLoad, this);
27549         ds.un("load", this.onLoad, this);
27550         ds.un("loadexception", this.onLoadError, this);
27551         ds.un("remove", this.updateInfo, this);
27552         ds.un("add", this.updateInfo, this);
27553         this.ds = undefined;
27554     },
27555
27556     /**
27557      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27558      * @param {Roo.data.Store} store The data store to bind
27559      */
27560     bind : function(ds){
27561         ds.on("beforeload", this.beforeLoad, this);
27562         ds.on("load", this.onLoad, this);
27563         ds.on("loadexception", this.onLoadError, this);
27564         ds.on("remove", this.updateInfo, this);
27565         ds.on("add", this.updateInfo, this);
27566         this.ds = ds;
27567     }
27568 });/*
27569  * - LGPL
27570  *
27571  * element
27572  * 
27573  */
27574
27575 /**
27576  * @class Roo.bootstrap.MessageBar
27577  * @extends Roo.bootstrap.Component
27578  * Bootstrap MessageBar class
27579  * @cfg {String} html contents of the MessageBar
27580  * @cfg {String} weight (info | success | warning | danger) default info
27581  * @cfg {String} beforeClass insert the bar before the given class
27582  * @cfg {Boolean} closable (true | false) default false
27583  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27584  * 
27585  * @constructor
27586  * Create a new Element
27587  * @param {Object} config The config object
27588  */
27589
27590 Roo.bootstrap.MessageBar = function(config){
27591     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27592 };
27593
27594 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27595     
27596     html: '',
27597     weight: 'info',
27598     closable: false,
27599     fixed: false,
27600     beforeClass: 'bootstrap-sticky-wrap',
27601     
27602     getAutoCreate : function(){
27603         
27604         var cfg = {
27605             tag: 'div',
27606             cls: 'alert alert-dismissable alert-' + this.weight,
27607             cn: [
27608                 {
27609                     tag: 'span',
27610                     cls: 'message',
27611                     html: this.html || ''
27612                 }
27613             ]
27614         };
27615         
27616         if(this.fixed){
27617             cfg.cls += ' alert-messages-fixed';
27618         }
27619         
27620         if(this.closable){
27621             cfg.cn.push({
27622                 tag: 'button',
27623                 cls: 'close',
27624                 html: 'x'
27625             });
27626         }
27627         
27628         return cfg;
27629     },
27630     
27631     onRender : function(ct, position)
27632     {
27633         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27634         
27635         if(!this.el){
27636             var cfg = Roo.apply({},  this.getAutoCreate());
27637             cfg.id = Roo.id();
27638             
27639             if (this.cls) {
27640                 cfg.cls += ' ' + this.cls;
27641             }
27642             if (this.style) {
27643                 cfg.style = this.style;
27644             }
27645             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27646             
27647             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27648         }
27649         
27650         this.el.select('>button.close').on('click', this.hide, this);
27651         
27652     },
27653     
27654     show : function()
27655     {
27656         if (!this.rendered) {
27657             this.render();
27658         }
27659         
27660         this.el.show();
27661         
27662         this.fireEvent('show', this);
27663         
27664     },
27665     
27666     hide : function()
27667     {
27668         if (!this.rendered) {
27669             this.render();
27670         }
27671         
27672         this.el.hide();
27673         
27674         this.fireEvent('hide', this);
27675     },
27676     
27677     update : function()
27678     {
27679 //        var e = this.el.dom.firstChild;
27680 //        
27681 //        if(this.closable){
27682 //            e = e.nextSibling;
27683 //        }
27684 //        
27685 //        e.data = this.html || '';
27686
27687         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27688     }
27689    
27690 });
27691
27692  
27693
27694      /*
27695  * - LGPL
27696  *
27697  * Graph
27698  * 
27699  */
27700
27701
27702 /**
27703  * @class Roo.bootstrap.Graph
27704  * @extends Roo.bootstrap.Component
27705  * Bootstrap Graph class
27706 > Prameters
27707  -sm {number} sm 4
27708  -md {number} md 5
27709  @cfg {String} graphtype  bar | vbar | pie
27710  @cfg {number} g_x coodinator | centre x (pie)
27711  @cfg {number} g_y coodinator | centre y (pie)
27712  @cfg {number} g_r radius (pie)
27713  @cfg {number} g_height height of the chart (respected by all elements in the set)
27714  @cfg {number} g_width width of the chart (respected by all elements in the set)
27715  @cfg {Object} title The title of the chart
27716     
27717  -{Array}  values
27718  -opts (object) options for the chart 
27719      o {
27720      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27721      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27722      o vgutter (number)
27723      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.
27724      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27725      o to
27726      o stretch (boolean)
27727      o }
27728  -opts (object) options for the pie
27729      o{
27730      o cut
27731      o startAngle (number)
27732      o endAngle (number)
27733      } 
27734  *
27735  * @constructor
27736  * Create a new Input
27737  * @param {Object} config The config object
27738  */
27739
27740 Roo.bootstrap.Graph = function(config){
27741     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27742     
27743     this.addEvents({
27744         // img events
27745         /**
27746          * @event click
27747          * The img click event for the img.
27748          * @param {Roo.EventObject} e
27749          */
27750         "click" : true
27751     });
27752 };
27753
27754 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27755     
27756     sm: 4,
27757     md: 5,
27758     graphtype: 'bar',
27759     g_height: 250,
27760     g_width: 400,
27761     g_x: 50,
27762     g_y: 50,
27763     g_r: 30,
27764     opts:{
27765         //g_colors: this.colors,
27766         g_type: 'soft',
27767         g_gutter: '20%'
27768
27769     },
27770     title : false,
27771
27772     getAutoCreate : function(){
27773         
27774         var cfg = {
27775             tag: 'div',
27776             html : null
27777         };
27778         
27779         
27780         return  cfg;
27781     },
27782
27783     onRender : function(ct,position){
27784         
27785         
27786         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27787         
27788         if (typeof(Raphael) == 'undefined') {
27789             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27790             return;
27791         }
27792         
27793         this.raphael = Raphael(this.el.dom);
27794         
27795                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27796                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27797                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27798                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27799                 /*
27800                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27801                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27802                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27803                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27804                 
27805                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27806                 r.barchart(330, 10, 300, 220, data1);
27807                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27808                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27809                 */
27810                 
27811                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27812                 // r.barchart(30, 30, 560, 250,  xdata, {
27813                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27814                 //     axis : "0 0 1 1",
27815                 //     axisxlabels :  xdata
27816                 //     //yvalues : cols,
27817                    
27818                 // });
27819 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27820 //        
27821 //        this.load(null,xdata,{
27822 //                axis : "0 0 1 1",
27823 //                axisxlabels :  xdata
27824 //                });
27825
27826     },
27827
27828     load : function(graphtype,xdata,opts)
27829     {
27830         this.raphael.clear();
27831         if(!graphtype) {
27832             graphtype = this.graphtype;
27833         }
27834         if(!opts){
27835             opts = this.opts;
27836         }
27837         var r = this.raphael,
27838             fin = function () {
27839                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27840             },
27841             fout = function () {
27842                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27843             },
27844             pfin = function() {
27845                 this.sector.stop();
27846                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27847
27848                 if (this.label) {
27849                     this.label[0].stop();
27850                     this.label[0].attr({ r: 7.5 });
27851                     this.label[1].attr({ "font-weight": 800 });
27852                 }
27853             },
27854             pfout = function() {
27855                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27856
27857                 if (this.label) {
27858                     this.label[0].animate({ r: 5 }, 500, "bounce");
27859                     this.label[1].attr({ "font-weight": 400 });
27860                 }
27861             };
27862
27863         switch(graphtype){
27864             case 'bar':
27865                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27866                 break;
27867             case 'hbar':
27868                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27869                 break;
27870             case 'pie':
27871 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27872 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27873 //            
27874                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27875                 
27876                 break;
27877
27878         }
27879         
27880         if(this.title){
27881             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27882         }
27883         
27884     },
27885     
27886     setTitle: function(o)
27887     {
27888         this.title = o;
27889     },
27890     
27891     initEvents: function() {
27892         
27893         if(!this.href){
27894             this.el.on('click', this.onClick, this);
27895         }
27896     },
27897     
27898     onClick : function(e)
27899     {
27900         Roo.log('img onclick');
27901         this.fireEvent('click', this, e);
27902     }
27903    
27904 });
27905
27906  
27907 /*
27908  * - LGPL
27909  *
27910  * numberBox
27911  * 
27912  */
27913 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27914
27915 /**
27916  * @class Roo.bootstrap.dash.NumberBox
27917  * @extends Roo.bootstrap.Component
27918  * Bootstrap NumberBox class
27919  * @cfg {String} headline Box headline
27920  * @cfg {String} content Box content
27921  * @cfg {String} icon Box icon
27922  * @cfg {String} footer Footer text
27923  * @cfg {String} fhref Footer href
27924  * 
27925  * @constructor
27926  * Create a new NumberBox
27927  * @param {Object} config The config object
27928  */
27929
27930
27931 Roo.bootstrap.dash.NumberBox = function(config){
27932     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27933     
27934 };
27935
27936 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27937     
27938     headline : '',
27939     content : '',
27940     icon : '',
27941     footer : '',
27942     fhref : '',
27943     ficon : '',
27944     
27945     getAutoCreate : function(){
27946         
27947         var cfg = {
27948             tag : 'div',
27949             cls : 'small-box ',
27950             cn : [
27951                 {
27952                     tag : 'div',
27953                     cls : 'inner',
27954                     cn :[
27955                         {
27956                             tag : 'h3',
27957                             cls : 'roo-headline',
27958                             html : this.headline
27959                         },
27960                         {
27961                             tag : 'p',
27962                             cls : 'roo-content',
27963                             html : this.content
27964                         }
27965                     ]
27966                 }
27967             ]
27968         };
27969         
27970         if(this.icon){
27971             cfg.cn.push({
27972                 tag : 'div',
27973                 cls : 'icon',
27974                 cn :[
27975                     {
27976                         tag : 'i',
27977                         cls : 'ion ' + this.icon
27978                     }
27979                 ]
27980             });
27981         }
27982         
27983         if(this.footer){
27984             var footer = {
27985                 tag : 'a',
27986                 cls : 'small-box-footer',
27987                 href : this.fhref || '#',
27988                 html : this.footer
27989             };
27990             
27991             cfg.cn.push(footer);
27992             
27993         }
27994         
27995         return  cfg;
27996     },
27997
27998     onRender : function(ct,position){
27999         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28000
28001
28002        
28003                 
28004     },
28005
28006     setHeadline: function (value)
28007     {
28008         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28009     },
28010     
28011     setFooter: function (value, href)
28012     {
28013         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28014         
28015         if(href){
28016             this.el.select('a.small-box-footer',true).first().attr('href', href);
28017         }
28018         
28019     },
28020
28021     setContent: function (value)
28022     {
28023         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28024     },
28025
28026     initEvents: function() 
28027     {   
28028         
28029     }
28030     
28031 });
28032
28033  
28034 /*
28035  * - LGPL
28036  *
28037  * TabBox
28038  * 
28039  */
28040 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28041
28042 /**
28043  * @class Roo.bootstrap.dash.TabBox
28044  * @extends Roo.bootstrap.Component
28045  * Bootstrap TabBox class
28046  * @cfg {String} title Title of the TabBox
28047  * @cfg {String} icon Icon of the TabBox
28048  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28049  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28050  * 
28051  * @constructor
28052  * Create a new TabBox
28053  * @param {Object} config The config object
28054  */
28055
28056
28057 Roo.bootstrap.dash.TabBox = function(config){
28058     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28059     this.addEvents({
28060         // raw events
28061         /**
28062          * @event addpane
28063          * When a pane is added
28064          * @param {Roo.bootstrap.dash.TabPane} pane
28065          */
28066         "addpane" : true,
28067         /**
28068          * @event activatepane
28069          * When a pane is activated
28070          * @param {Roo.bootstrap.dash.TabPane} pane
28071          */
28072         "activatepane" : true
28073         
28074          
28075     });
28076     
28077     this.panes = [];
28078 };
28079
28080 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28081
28082     title : '',
28083     icon : false,
28084     showtabs : true,
28085     tabScrollable : false,
28086     
28087     getChildContainer : function()
28088     {
28089         return this.el.select('.tab-content', true).first();
28090     },
28091     
28092     getAutoCreate : function(){
28093         
28094         var header = {
28095             tag: 'li',
28096             cls: 'pull-left header',
28097             html: this.title,
28098             cn : []
28099         };
28100         
28101         if(this.icon){
28102             header.cn.push({
28103                 tag: 'i',
28104                 cls: 'fa ' + this.icon
28105             });
28106         }
28107         
28108         var h = {
28109             tag: 'ul',
28110             cls: 'nav nav-tabs pull-right',
28111             cn: [
28112                 header
28113             ]
28114         };
28115         
28116         if(this.tabScrollable){
28117             h = {
28118                 tag: 'div',
28119                 cls: 'tab-header',
28120                 cn: [
28121                     {
28122                         tag: 'ul',
28123                         cls: 'nav nav-tabs pull-right',
28124                         cn: [
28125                             header
28126                         ]
28127                     }
28128                 ]
28129             };
28130         }
28131         
28132         var cfg = {
28133             tag: 'div',
28134             cls: 'nav-tabs-custom',
28135             cn: [
28136                 h,
28137                 {
28138                     tag: 'div',
28139                     cls: 'tab-content no-padding',
28140                     cn: []
28141                 }
28142             ]
28143         };
28144
28145         return  cfg;
28146     },
28147     initEvents : function()
28148     {
28149         //Roo.log('add add pane handler');
28150         this.on('addpane', this.onAddPane, this);
28151     },
28152      /**
28153      * Updates the box title
28154      * @param {String} html to set the title to.
28155      */
28156     setTitle : function(value)
28157     {
28158         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28159     },
28160     onAddPane : function(pane)
28161     {
28162         this.panes.push(pane);
28163         //Roo.log('addpane');
28164         //Roo.log(pane);
28165         // tabs are rendere left to right..
28166         if(!this.showtabs){
28167             return;
28168         }
28169         
28170         var ctr = this.el.select('.nav-tabs', true).first();
28171          
28172          
28173         var existing = ctr.select('.nav-tab',true);
28174         var qty = existing.getCount();;
28175         
28176         
28177         var tab = ctr.createChild({
28178             tag : 'li',
28179             cls : 'nav-tab' + (qty ? '' : ' active'),
28180             cn : [
28181                 {
28182                     tag : 'a',
28183                     href:'#',
28184                     html : pane.title
28185                 }
28186             ]
28187         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28188         pane.tab = tab;
28189         
28190         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28191         if (!qty) {
28192             pane.el.addClass('active');
28193         }
28194         
28195                 
28196     },
28197     onTabClick : function(ev,un,ob,pane)
28198     {
28199         //Roo.log('tab - prev default');
28200         ev.preventDefault();
28201         
28202         
28203         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28204         pane.tab.addClass('active');
28205         //Roo.log(pane.title);
28206         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28207         // technically we should have a deactivate event.. but maybe add later.
28208         // and it should not de-activate the selected tab...
28209         this.fireEvent('activatepane', pane);
28210         pane.el.addClass('active');
28211         pane.fireEvent('activate');
28212         
28213         
28214     },
28215     
28216     getActivePane : function()
28217     {
28218         var r = false;
28219         Roo.each(this.panes, function(p) {
28220             if(p.el.hasClass('active')){
28221                 r = p;
28222                 return false;
28223             }
28224             
28225             return;
28226         });
28227         
28228         return r;
28229     }
28230     
28231     
28232 });
28233
28234  
28235 /*
28236  * - LGPL
28237  *
28238  * Tab pane
28239  * 
28240  */
28241 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28242 /**
28243  * @class Roo.bootstrap.TabPane
28244  * @extends Roo.bootstrap.Component
28245  * Bootstrap TabPane class
28246  * @cfg {Boolean} active (false | true) Default false
28247  * @cfg {String} title title of panel
28248
28249  * 
28250  * @constructor
28251  * Create a new TabPane
28252  * @param {Object} config The config object
28253  */
28254
28255 Roo.bootstrap.dash.TabPane = function(config){
28256     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28257     
28258     this.addEvents({
28259         // raw events
28260         /**
28261          * @event activate
28262          * When a pane is activated
28263          * @param {Roo.bootstrap.dash.TabPane} pane
28264          */
28265         "activate" : true
28266          
28267     });
28268 };
28269
28270 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28271     
28272     active : false,
28273     title : '',
28274     
28275     // the tabBox that this is attached to.
28276     tab : false,
28277      
28278     getAutoCreate : function() 
28279     {
28280         var cfg = {
28281             tag: 'div',
28282             cls: 'tab-pane'
28283         };
28284         
28285         if(this.active){
28286             cfg.cls += ' active';
28287         }
28288         
28289         return cfg;
28290     },
28291     initEvents  : function()
28292     {
28293         //Roo.log('trigger add pane handler');
28294         this.parent().fireEvent('addpane', this)
28295     },
28296     
28297      /**
28298      * Updates the tab title 
28299      * @param {String} html to set the title to.
28300      */
28301     setTitle: function(str)
28302     {
28303         if (!this.tab) {
28304             return;
28305         }
28306         this.title = str;
28307         this.tab.select('a', true).first().dom.innerHTML = str;
28308         
28309     }
28310     
28311     
28312     
28313 });
28314
28315  
28316
28317
28318  /*
28319  * - LGPL
28320  *
28321  * menu
28322  * 
28323  */
28324 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28325
28326 /**
28327  * @class Roo.bootstrap.menu.Menu
28328  * @extends Roo.bootstrap.Component
28329  * Bootstrap Menu class - container for Menu
28330  * @cfg {String} html Text of the menu
28331  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28332  * @cfg {String} icon Font awesome icon
28333  * @cfg {String} pos Menu align to (top | bottom) default bottom
28334  * 
28335  * 
28336  * @constructor
28337  * Create a new Menu
28338  * @param {Object} config The config object
28339  */
28340
28341
28342 Roo.bootstrap.menu.Menu = function(config){
28343     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28344     
28345     this.addEvents({
28346         /**
28347          * @event beforeshow
28348          * Fires before this menu is displayed
28349          * @param {Roo.bootstrap.menu.Menu} this
28350          */
28351         beforeshow : true,
28352         /**
28353          * @event beforehide
28354          * Fires before this menu is hidden
28355          * @param {Roo.bootstrap.menu.Menu} this
28356          */
28357         beforehide : true,
28358         /**
28359          * @event show
28360          * Fires after this menu is displayed
28361          * @param {Roo.bootstrap.menu.Menu} this
28362          */
28363         show : true,
28364         /**
28365          * @event hide
28366          * Fires after this menu is hidden
28367          * @param {Roo.bootstrap.menu.Menu} this
28368          */
28369         hide : true,
28370         /**
28371          * @event click
28372          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28373          * @param {Roo.bootstrap.menu.Menu} this
28374          * @param {Roo.EventObject} e
28375          */
28376         click : true
28377     });
28378     
28379 };
28380
28381 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28382     
28383     submenu : false,
28384     html : '',
28385     weight : 'default',
28386     icon : false,
28387     pos : 'bottom',
28388     
28389     
28390     getChildContainer : function() {
28391         if(this.isSubMenu){
28392             return this.el;
28393         }
28394         
28395         return this.el.select('ul.dropdown-menu', true).first();  
28396     },
28397     
28398     getAutoCreate : function()
28399     {
28400         var text = [
28401             {
28402                 tag : 'span',
28403                 cls : 'roo-menu-text',
28404                 html : this.html
28405             }
28406         ];
28407         
28408         if(this.icon){
28409             text.unshift({
28410                 tag : 'i',
28411                 cls : 'fa ' + this.icon
28412             })
28413         }
28414         
28415         
28416         var cfg = {
28417             tag : 'div',
28418             cls : 'btn-group',
28419             cn : [
28420                 {
28421                     tag : 'button',
28422                     cls : 'dropdown-button btn btn-' + this.weight,
28423                     cn : text
28424                 },
28425                 {
28426                     tag : 'button',
28427                     cls : 'dropdown-toggle btn btn-' + this.weight,
28428                     cn : [
28429                         {
28430                             tag : 'span',
28431                             cls : 'caret'
28432                         }
28433                     ]
28434                 },
28435                 {
28436                     tag : 'ul',
28437                     cls : 'dropdown-menu'
28438                 }
28439             ]
28440             
28441         };
28442         
28443         if(this.pos == 'top'){
28444             cfg.cls += ' dropup';
28445         }
28446         
28447         if(this.isSubMenu){
28448             cfg = {
28449                 tag : 'ul',
28450                 cls : 'dropdown-menu'
28451             }
28452         }
28453         
28454         return cfg;
28455     },
28456     
28457     onRender : function(ct, position)
28458     {
28459         this.isSubMenu = ct.hasClass('dropdown-submenu');
28460         
28461         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28462     },
28463     
28464     initEvents : function() 
28465     {
28466         if(this.isSubMenu){
28467             return;
28468         }
28469         
28470         this.hidden = true;
28471         
28472         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28473         this.triggerEl.on('click', this.onTriggerPress, this);
28474         
28475         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28476         this.buttonEl.on('click', this.onClick, this);
28477         
28478     },
28479     
28480     list : function()
28481     {
28482         if(this.isSubMenu){
28483             return this.el;
28484         }
28485         
28486         return this.el.select('ul.dropdown-menu', true).first();
28487     },
28488     
28489     onClick : function(e)
28490     {
28491         this.fireEvent("click", this, e);
28492     },
28493     
28494     onTriggerPress  : function(e)
28495     {   
28496         if (this.isVisible()) {
28497             this.hide();
28498         } else {
28499             this.show();
28500         }
28501     },
28502     
28503     isVisible : function(){
28504         return !this.hidden;
28505     },
28506     
28507     show : function()
28508     {
28509         this.fireEvent("beforeshow", this);
28510         
28511         this.hidden = false;
28512         this.el.addClass('open');
28513         
28514         Roo.get(document).on("mouseup", this.onMouseUp, this);
28515         
28516         this.fireEvent("show", this);
28517         
28518         
28519     },
28520     
28521     hide : function()
28522     {
28523         this.fireEvent("beforehide", this);
28524         
28525         this.hidden = true;
28526         this.el.removeClass('open');
28527         
28528         Roo.get(document).un("mouseup", this.onMouseUp);
28529         
28530         this.fireEvent("hide", this);
28531     },
28532     
28533     onMouseUp : function()
28534     {
28535         this.hide();
28536     }
28537     
28538 });
28539
28540  
28541  /*
28542  * - LGPL
28543  *
28544  * menu item
28545  * 
28546  */
28547 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28548
28549 /**
28550  * @class Roo.bootstrap.menu.Item
28551  * @extends Roo.bootstrap.Component
28552  * Bootstrap MenuItem class
28553  * @cfg {Boolean} submenu (true | false) default false
28554  * @cfg {String} html text of the item
28555  * @cfg {String} href the link
28556  * @cfg {Boolean} disable (true | false) default false
28557  * @cfg {Boolean} preventDefault (true | false) default true
28558  * @cfg {String} icon Font awesome icon
28559  * @cfg {String} pos Submenu align to (left | right) default right 
28560  * 
28561  * 
28562  * @constructor
28563  * Create a new Item
28564  * @param {Object} config The config object
28565  */
28566
28567
28568 Roo.bootstrap.menu.Item = function(config){
28569     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28570     this.addEvents({
28571         /**
28572          * @event mouseover
28573          * Fires when the mouse is hovering over this menu
28574          * @param {Roo.bootstrap.menu.Item} this
28575          * @param {Roo.EventObject} e
28576          */
28577         mouseover : true,
28578         /**
28579          * @event mouseout
28580          * Fires when the mouse exits this menu
28581          * @param {Roo.bootstrap.menu.Item} this
28582          * @param {Roo.EventObject} e
28583          */
28584         mouseout : true,
28585         // raw events
28586         /**
28587          * @event click
28588          * The raw click event for the entire grid.
28589          * @param {Roo.EventObject} e
28590          */
28591         click : true
28592     });
28593 };
28594
28595 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28596     
28597     submenu : false,
28598     href : '',
28599     html : '',
28600     preventDefault: true,
28601     disable : false,
28602     icon : false,
28603     pos : 'right',
28604     
28605     getAutoCreate : function()
28606     {
28607         var text = [
28608             {
28609                 tag : 'span',
28610                 cls : 'roo-menu-item-text',
28611                 html : this.html
28612             }
28613         ];
28614         
28615         if(this.icon){
28616             text.unshift({
28617                 tag : 'i',
28618                 cls : 'fa ' + this.icon
28619             })
28620         }
28621         
28622         var cfg = {
28623             tag : 'li',
28624             cn : [
28625                 {
28626                     tag : 'a',
28627                     href : this.href || '#',
28628                     cn : text
28629                 }
28630             ]
28631         };
28632         
28633         if(this.disable){
28634             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28635         }
28636         
28637         if(this.submenu){
28638             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28639             
28640             if(this.pos == 'left'){
28641                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28642             }
28643         }
28644         
28645         return cfg;
28646     },
28647     
28648     initEvents : function() 
28649     {
28650         this.el.on('mouseover', this.onMouseOver, this);
28651         this.el.on('mouseout', this.onMouseOut, this);
28652         
28653         this.el.select('a', true).first().on('click', this.onClick, this);
28654         
28655     },
28656     
28657     onClick : function(e)
28658     {
28659         if(this.preventDefault){
28660             e.preventDefault();
28661         }
28662         
28663         this.fireEvent("click", this, e);
28664     },
28665     
28666     onMouseOver : function(e)
28667     {
28668         if(this.submenu && this.pos == 'left'){
28669             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28670         }
28671         
28672         this.fireEvent("mouseover", this, e);
28673     },
28674     
28675     onMouseOut : function(e)
28676     {
28677         this.fireEvent("mouseout", this, e);
28678     }
28679 });
28680
28681  
28682
28683  /*
28684  * - LGPL
28685  *
28686  * menu separator
28687  * 
28688  */
28689 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28690
28691 /**
28692  * @class Roo.bootstrap.menu.Separator
28693  * @extends Roo.bootstrap.Component
28694  * Bootstrap Separator class
28695  * 
28696  * @constructor
28697  * Create a new Separator
28698  * @param {Object} config The config object
28699  */
28700
28701
28702 Roo.bootstrap.menu.Separator = function(config){
28703     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28704 };
28705
28706 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28707     
28708     getAutoCreate : function(){
28709         var cfg = {
28710             tag : 'li',
28711             cls: 'divider'
28712         };
28713         
28714         return cfg;
28715     }
28716    
28717 });
28718
28719  
28720
28721  /*
28722  * - LGPL
28723  *
28724  * Tooltip
28725  * 
28726  */
28727
28728 /**
28729  * @class Roo.bootstrap.Tooltip
28730  * Bootstrap Tooltip class
28731  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28732  * to determine which dom element triggers the tooltip.
28733  * 
28734  * It needs to add support for additional attributes like tooltip-position
28735  * 
28736  * @constructor
28737  * Create a new Toolti
28738  * @param {Object} config The config object
28739  */
28740
28741 Roo.bootstrap.Tooltip = function(config){
28742     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28743     
28744     this.alignment = Roo.bootstrap.Tooltip.alignment;
28745     
28746     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28747         this.alignment = config.alignment;
28748     }
28749     
28750 };
28751
28752 Roo.apply(Roo.bootstrap.Tooltip, {
28753     /**
28754      * @function init initialize tooltip monitoring.
28755      * @static
28756      */
28757     currentEl : false,
28758     currentTip : false,
28759     currentRegion : false,
28760     
28761     //  init : delay?
28762     
28763     init : function()
28764     {
28765         Roo.get(document).on('mouseover', this.enter ,this);
28766         Roo.get(document).on('mouseout', this.leave, this);
28767          
28768         
28769         this.currentTip = new Roo.bootstrap.Tooltip();
28770     },
28771     
28772     enter : function(ev)
28773     {
28774         var dom = ev.getTarget();
28775         
28776         //Roo.log(['enter',dom]);
28777         var el = Roo.fly(dom);
28778         if (this.currentEl) {
28779             //Roo.log(dom);
28780             //Roo.log(this.currentEl);
28781             //Roo.log(this.currentEl.contains(dom));
28782             if (this.currentEl == el) {
28783                 return;
28784             }
28785             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28786                 return;
28787             }
28788
28789         }
28790         
28791         if (this.currentTip.el) {
28792             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28793         }    
28794         //Roo.log(ev);
28795         
28796         if(!el || el.dom == document){
28797             return;
28798         }
28799         
28800         var bindEl = el;
28801         
28802         // you can not look for children, as if el is the body.. then everythign is the child..
28803         if (!el.attr('tooltip')) { //
28804             if (!el.select("[tooltip]").elements.length) {
28805                 return;
28806             }
28807             // is the mouse over this child...?
28808             bindEl = el.select("[tooltip]").first();
28809             var xy = ev.getXY();
28810             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28811                 //Roo.log("not in region.");
28812                 return;
28813             }
28814             //Roo.log("child element over..");
28815             
28816         }
28817         this.currentEl = bindEl;
28818         this.currentTip.bind(bindEl);
28819         this.currentRegion = Roo.lib.Region.getRegion(dom);
28820         this.currentTip.enter();
28821         
28822     },
28823     leave : function(ev)
28824     {
28825         var dom = ev.getTarget();
28826         //Roo.log(['leave',dom]);
28827         if (!this.currentEl) {
28828             return;
28829         }
28830         
28831         
28832         if (dom != this.currentEl.dom) {
28833             return;
28834         }
28835         var xy = ev.getXY();
28836         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28837             return;
28838         }
28839         // only activate leave if mouse cursor is outside... bounding box..
28840         
28841         
28842         
28843         
28844         if (this.currentTip) {
28845             this.currentTip.leave();
28846         }
28847         //Roo.log('clear currentEl');
28848         this.currentEl = false;
28849         
28850         
28851     },
28852     alignment : {
28853         'left' : ['r-l', [-2,0], 'right'],
28854         'right' : ['l-r', [2,0], 'left'],
28855         'bottom' : ['t-b', [0,2], 'top'],
28856         'top' : [ 'b-t', [0,-2], 'bottom']
28857     }
28858     
28859 });
28860
28861
28862 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28863     
28864     
28865     bindEl : false,
28866     
28867     delay : null, // can be { show : 300 , hide: 500}
28868     
28869     timeout : null,
28870     
28871     hoverState : null, //???
28872     
28873     placement : 'bottom', 
28874     
28875     alignment : false,
28876     
28877     getAutoCreate : function(){
28878     
28879         var cfg = {
28880            cls : 'tooltip',   
28881            role : 'tooltip',
28882            cn : [
28883                 {
28884                     cls : 'tooltip-arrow arrow'
28885                 },
28886                 {
28887                     cls : 'tooltip-inner'
28888                 }
28889            ]
28890         };
28891         
28892         return cfg;
28893     },
28894     bind : function(el)
28895     {
28896         this.bindEl = el;
28897     },
28898     
28899     initEvents : function()
28900     {
28901         this.arrowEl = this.el.select('.arrow', true).first();
28902         this.innerEl = this.el.select('.tooltip-inner', true).first();
28903     },
28904     
28905     enter : function () {
28906        
28907         if (this.timeout != null) {
28908             clearTimeout(this.timeout);
28909         }
28910         
28911         this.hoverState = 'in';
28912          //Roo.log("enter - show");
28913         if (!this.delay || !this.delay.show) {
28914             this.show();
28915             return;
28916         }
28917         var _t = this;
28918         this.timeout = setTimeout(function () {
28919             if (_t.hoverState == 'in') {
28920                 _t.show();
28921             }
28922         }, this.delay.show);
28923     },
28924     leave : function()
28925     {
28926         clearTimeout(this.timeout);
28927     
28928         this.hoverState = 'out';
28929          if (!this.delay || !this.delay.hide) {
28930             this.hide();
28931             return;
28932         }
28933        
28934         var _t = this;
28935         this.timeout = setTimeout(function () {
28936             //Roo.log("leave - timeout");
28937             
28938             if (_t.hoverState == 'out') {
28939                 _t.hide();
28940                 Roo.bootstrap.Tooltip.currentEl = false;
28941             }
28942         }, delay);
28943     },
28944     
28945     show : function (msg)
28946     {
28947         if (!this.el) {
28948             this.render(document.body);
28949         }
28950         // set content.
28951         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28952         
28953         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28954         
28955         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28956         
28957         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28958                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28959         
28960         var placement = typeof this.placement == 'function' ?
28961             this.placement.call(this, this.el, on_el) :
28962             this.placement;
28963             
28964         var autoToken = /\s?auto?\s?/i;
28965         var autoPlace = autoToken.test(placement);
28966         if (autoPlace) {
28967             placement = placement.replace(autoToken, '') || 'top';
28968         }
28969         
28970         //this.el.detach()
28971         //this.el.setXY([0,0]);
28972         this.el.show();
28973         //this.el.dom.style.display='block';
28974         
28975         //this.el.appendTo(on_el);
28976         
28977         var p = this.getPosition();
28978         var box = this.el.getBox();
28979         
28980         if (autoPlace) {
28981             // fixme..
28982         }
28983         
28984         var align = this.alignment[placement];
28985         
28986         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28987         
28988         if(placement == 'top' || placement == 'bottom'){
28989             if(xy[0] < 0){
28990                 placement = 'right';
28991             }
28992             
28993             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28994                 placement = 'left';
28995             }
28996             
28997             var scroll = Roo.select('body', true).first().getScroll();
28998             
28999             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29000                 placement = 'top';
29001             }
29002             
29003             align = this.alignment[placement];
29004             
29005             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29006             
29007         }
29008         
29009         this.el.alignTo(this.bindEl, align[0],align[1]);
29010         //var arrow = this.el.select('.arrow',true).first();
29011         //arrow.set(align[2], 
29012         
29013         this.el.addClass(placement);
29014         this.el.addClass("bs-tooltip-"+ placement);
29015         
29016         this.el.addClass('in fade show');
29017         
29018         this.hoverState = null;
29019         
29020         if (this.el.hasClass('fade')) {
29021             // fade it?
29022         }
29023         
29024         
29025         
29026         
29027         
29028     },
29029     hide : function()
29030     {
29031          
29032         if (!this.el) {
29033             return;
29034         }
29035         //this.el.setXY([0,0]);
29036         this.el.removeClass(['show', 'in']);
29037         //this.el.hide();
29038         
29039     }
29040     
29041 });
29042  
29043
29044  /*
29045  * - LGPL
29046  *
29047  * Location Picker
29048  * 
29049  */
29050
29051 /**
29052  * @class Roo.bootstrap.LocationPicker
29053  * @extends Roo.bootstrap.Component
29054  * Bootstrap LocationPicker class
29055  * @cfg {Number} latitude Position when init default 0
29056  * @cfg {Number} longitude Position when init default 0
29057  * @cfg {Number} zoom default 15
29058  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29059  * @cfg {Boolean} mapTypeControl default false
29060  * @cfg {Boolean} disableDoubleClickZoom default false
29061  * @cfg {Boolean} scrollwheel default true
29062  * @cfg {Boolean} streetViewControl default false
29063  * @cfg {Number} radius default 0
29064  * @cfg {String} locationName
29065  * @cfg {Boolean} draggable default true
29066  * @cfg {Boolean} enableAutocomplete default false
29067  * @cfg {Boolean} enableReverseGeocode default true
29068  * @cfg {String} markerTitle
29069  * 
29070  * @constructor
29071  * Create a new LocationPicker
29072  * @param {Object} config The config object
29073  */
29074
29075
29076 Roo.bootstrap.LocationPicker = function(config){
29077     
29078     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29079     
29080     this.addEvents({
29081         /**
29082          * @event initial
29083          * Fires when the picker initialized.
29084          * @param {Roo.bootstrap.LocationPicker} this
29085          * @param {Google Location} location
29086          */
29087         initial : true,
29088         /**
29089          * @event positionchanged
29090          * Fires when the picker position changed.
29091          * @param {Roo.bootstrap.LocationPicker} this
29092          * @param {Google Location} location
29093          */
29094         positionchanged : true,
29095         /**
29096          * @event resize
29097          * Fires when the map resize.
29098          * @param {Roo.bootstrap.LocationPicker} this
29099          */
29100         resize : true,
29101         /**
29102          * @event show
29103          * Fires when the map show.
29104          * @param {Roo.bootstrap.LocationPicker} this
29105          */
29106         show : true,
29107         /**
29108          * @event hide
29109          * Fires when the map hide.
29110          * @param {Roo.bootstrap.LocationPicker} this
29111          */
29112         hide : true,
29113         /**
29114          * @event mapClick
29115          * Fires when click the map.
29116          * @param {Roo.bootstrap.LocationPicker} this
29117          * @param {Map event} e
29118          */
29119         mapClick : true,
29120         /**
29121          * @event mapRightClick
29122          * Fires when right click the map.
29123          * @param {Roo.bootstrap.LocationPicker} this
29124          * @param {Map event} e
29125          */
29126         mapRightClick : true,
29127         /**
29128          * @event markerClick
29129          * Fires when click the marker.
29130          * @param {Roo.bootstrap.LocationPicker} this
29131          * @param {Map event} e
29132          */
29133         markerClick : true,
29134         /**
29135          * @event markerRightClick
29136          * Fires when right click the marker.
29137          * @param {Roo.bootstrap.LocationPicker} this
29138          * @param {Map event} e
29139          */
29140         markerRightClick : true,
29141         /**
29142          * @event OverlayViewDraw
29143          * Fires when OverlayView Draw
29144          * @param {Roo.bootstrap.LocationPicker} this
29145          */
29146         OverlayViewDraw : true,
29147         /**
29148          * @event OverlayViewOnAdd
29149          * Fires when OverlayView Draw
29150          * @param {Roo.bootstrap.LocationPicker} this
29151          */
29152         OverlayViewOnAdd : true,
29153         /**
29154          * @event OverlayViewOnRemove
29155          * Fires when OverlayView Draw
29156          * @param {Roo.bootstrap.LocationPicker} this
29157          */
29158         OverlayViewOnRemove : true,
29159         /**
29160          * @event OverlayViewShow
29161          * Fires when OverlayView Draw
29162          * @param {Roo.bootstrap.LocationPicker} this
29163          * @param {Pixel} cpx
29164          */
29165         OverlayViewShow : true,
29166         /**
29167          * @event OverlayViewHide
29168          * Fires when OverlayView Draw
29169          * @param {Roo.bootstrap.LocationPicker} this
29170          */
29171         OverlayViewHide : true,
29172         /**
29173          * @event loadexception
29174          * Fires when load google lib failed.
29175          * @param {Roo.bootstrap.LocationPicker} this
29176          */
29177         loadexception : true
29178     });
29179         
29180 };
29181
29182 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29183     
29184     gMapContext: false,
29185     
29186     latitude: 0,
29187     longitude: 0,
29188     zoom: 15,
29189     mapTypeId: false,
29190     mapTypeControl: false,
29191     disableDoubleClickZoom: false,
29192     scrollwheel: true,
29193     streetViewControl: false,
29194     radius: 0,
29195     locationName: '',
29196     draggable: true,
29197     enableAutocomplete: false,
29198     enableReverseGeocode: true,
29199     markerTitle: '',
29200     
29201     getAutoCreate: function()
29202     {
29203
29204         var cfg = {
29205             tag: 'div',
29206             cls: 'roo-location-picker'
29207         };
29208         
29209         return cfg
29210     },
29211     
29212     initEvents: function(ct, position)
29213     {       
29214         if(!this.el.getWidth() || this.isApplied()){
29215             return;
29216         }
29217         
29218         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29219         
29220         this.initial();
29221     },
29222     
29223     initial: function()
29224     {
29225         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29226             this.fireEvent('loadexception', this);
29227             return;
29228         }
29229         
29230         if(!this.mapTypeId){
29231             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29232         }
29233         
29234         this.gMapContext = this.GMapContext();
29235         
29236         this.initOverlayView();
29237         
29238         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29239         
29240         var _this = this;
29241                 
29242         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29243             _this.setPosition(_this.gMapContext.marker.position);
29244         });
29245         
29246         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29247             _this.fireEvent('mapClick', this, event);
29248             
29249         });
29250
29251         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29252             _this.fireEvent('mapRightClick', this, event);
29253             
29254         });
29255         
29256         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29257             _this.fireEvent('markerClick', this, event);
29258             
29259         });
29260
29261         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29262             _this.fireEvent('markerRightClick', this, event);
29263             
29264         });
29265         
29266         this.setPosition(this.gMapContext.location);
29267         
29268         this.fireEvent('initial', this, this.gMapContext.location);
29269     },
29270     
29271     initOverlayView: function()
29272     {
29273         var _this = this;
29274         
29275         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29276             
29277             draw: function()
29278             {
29279                 _this.fireEvent('OverlayViewDraw', _this);
29280             },
29281             
29282             onAdd: function()
29283             {
29284                 _this.fireEvent('OverlayViewOnAdd', _this);
29285             },
29286             
29287             onRemove: function()
29288             {
29289                 _this.fireEvent('OverlayViewOnRemove', _this);
29290             },
29291             
29292             show: function(cpx)
29293             {
29294                 _this.fireEvent('OverlayViewShow', _this, cpx);
29295             },
29296             
29297             hide: function()
29298             {
29299                 _this.fireEvent('OverlayViewHide', _this);
29300             }
29301             
29302         });
29303     },
29304     
29305     fromLatLngToContainerPixel: function(event)
29306     {
29307         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29308     },
29309     
29310     isApplied: function() 
29311     {
29312         return this.getGmapContext() == false ? false : true;
29313     },
29314     
29315     getGmapContext: function() 
29316     {
29317         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29318     },
29319     
29320     GMapContext: function() 
29321     {
29322         var position = new google.maps.LatLng(this.latitude, this.longitude);
29323         
29324         var _map = new google.maps.Map(this.el.dom, {
29325             center: position,
29326             zoom: this.zoom,
29327             mapTypeId: this.mapTypeId,
29328             mapTypeControl: this.mapTypeControl,
29329             disableDoubleClickZoom: this.disableDoubleClickZoom,
29330             scrollwheel: this.scrollwheel,
29331             streetViewControl: this.streetViewControl,
29332             locationName: this.locationName,
29333             draggable: this.draggable,
29334             enableAutocomplete: this.enableAutocomplete,
29335             enableReverseGeocode: this.enableReverseGeocode
29336         });
29337         
29338         var _marker = new google.maps.Marker({
29339             position: position,
29340             map: _map,
29341             title: this.markerTitle,
29342             draggable: this.draggable
29343         });
29344         
29345         return {
29346             map: _map,
29347             marker: _marker,
29348             circle: null,
29349             location: position,
29350             radius: this.radius,
29351             locationName: this.locationName,
29352             addressComponents: {
29353                 formatted_address: null,
29354                 addressLine1: null,
29355                 addressLine2: null,
29356                 streetName: null,
29357                 streetNumber: null,
29358                 city: null,
29359                 district: null,
29360                 state: null,
29361                 stateOrProvince: null
29362             },
29363             settings: this,
29364             domContainer: this.el.dom,
29365             geodecoder: new google.maps.Geocoder()
29366         };
29367     },
29368     
29369     drawCircle: function(center, radius, options) 
29370     {
29371         if (this.gMapContext.circle != null) {
29372             this.gMapContext.circle.setMap(null);
29373         }
29374         if (radius > 0) {
29375             radius *= 1;
29376             options = Roo.apply({}, options, {
29377                 strokeColor: "#0000FF",
29378                 strokeOpacity: .35,
29379                 strokeWeight: 2,
29380                 fillColor: "#0000FF",
29381                 fillOpacity: .2
29382             });
29383             
29384             options.map = this.gMapContext.map;
29385             options.radius = radius;
29386             options.center = center;
29387             this.gMapContext.circle = new google.maps.Circle(options);
29388             return this.gMapContext.circle;
29389         }
29390         
29391         return null;
29392     },
29393     
29394     setPosition: function(location) 
29395     {
29396         this.gMapContext.location = location;
29397         this.gMapContext.marker.setPosition(location);
29398         this.gMapContext.map.panTo(location);
29399         this.drawCircle(location, this.gMapContext.radius, {});
29400         
29401         var _this = this;
29402         
29403         if (this.gMapContext.settings.enableReverseGeocode) {
29404             this.gMapContext.geodecoder.geocode({
29405                 latLng: this.gMapContext.location
29406             }, function(results, status) {
29407                 
29408                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29409                     _this.gMapContext.locationName = results[0].formatted_address;
29410                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29411                     
29412                     _this.fireEvent('positionchanged', this, location);
29413                 }
29414             });
29415             
29416             return;
29417         }
29418         
29419         this.fireEvent('positionchanged', this, location);
29420     },
29421     
29422     resize: function()
29423     {
29424         google.maps.event.trigger(this.gMapContext.map, "resize");
29425         
29426         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29427         
29428         this.fireEvent('resize', this);
29429     },
29430     
29431     setPositionByLatLng: function(latitude, longitude)
29432     {
29433         this.setPosition(new google.maps.LatLng(latitude, longitude));
29434     },
29435     
29436     getCurrentPosition: function() 
29437     {
29438         return {
29439             latitude: this.gMapContext.location.lat(),
29440             longitude: this.gMapContext.location.lng()
29441         };
29442     },
29443     
29444     getAddressName: function() 
29445     {
29446         return this.gMapContext.locationName;
29447     },
29448     
29449     getAddressComponents: function() 
29450     {
29451         return this.gMapContext.addressComponents;
29452     },
29453     
29454     address_component_from_google_geocode: function(address_components) 
29455     {
29456         var result = {};
29457         
29458         for (var i = 0; i < address_components.length; i++) {
29459             var component = address_components[i];
29460             if (component.types.indexOf("postal_code") >= 0) {
29461                 result.postalCode = component.short_name;
29462             } else if (component.types.indexOf("street_number") >= 0) {
29463                 result.streetNumber = component.short_name;
29464             } else if (component.types.indexOf("route") >= 0) {
29465                 result.streetName = component.short_name;
29466             } else if (component.types.indexOf("neighborhood") >= 0) {
29467                 result.city = component.short_name;
29468             } else if (component.types.indexOf("locality") >= 0) {
29469                 result.city = component.short_name;
29470             } else if (component.types.indexOf("sublocality") >= 0) {
29471                 result.district = component.short_name;
29472             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29473                 result.stateOrProvince = component.short_name;
29474             } else if (component.types.indexOf("country") >= 0) {
29475                 result.country = component.short_name;
29476             }
29477         }
29478         
29479         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29480         result.addressLine2 = "";
29481         return result;
29482     },
29483     
29484     setZoomLevel: function(zoom)
29485     {
29486         this.gMapContext.map.setZoom(zoom);
29487     },
29488     
29489     show: function()
29490     {
29491         if(!this.el){
29492             return;
29493         }
29494         
29495         this.el.show();
29496         
29497         this.resize();
29498         
29499         this.fireEvent('show', this);
29500     },
29501     
29502     hide: function()
29503     {
29504         if(!this.el){
29505             return;
29506         }
29507         
29508         this.el.hide();
29509         
29510         this.fireEvent('hide', this);
29511     }
29512     
29513 });
29514
29515 Roo.apply(Roo.bootstrap.LocationPicker, {
29516     
29517     OverlayView : function(map, options)
29518     {
29519         options = options || {};
29520         
29521         this.setMap(map);
29522     }
29523     
29524     
29525 });/**
29526  * @class Roo.bootstrap.Alert
29527  * @extends Roo.bootstrap.Component
29528  * Bootstrap Alert class - shows an alert area box
29529  * eg
29530  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29531   Enter a valid email address
29532 </div>
29533  * @licence LGPL
29534  * @cfg {String} title The title of alert
29535  * @cfg {String} html The content of alert
29536  * @cfg {String} weight (  success | info | warning | danger )
29537  * @cfg {String} faicon font-awesomeicon
29538  * 
29539  * @constructor
29540  * Create a new alert
29541  * @param {Object} config The config object
29542  */
29543
29544
29545 Roo.bootstrap.Alert = function(config){
29546     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29547     
29548 };
29549
29550 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29551     
29552     title: '',
29553     html: '',
29554     weight: false,
29555     faicon: false,
29556     
29557     getAutoCreate : function()
29558     {
29559         
29560         var cfg = {
29561             tag : 'div',
29562             cls : 'alert',
29563             cn : [
29564                 {
29565                     tag : 'i',
29566                     cls : 'roo-alert-icon'
29567                     
29568                 },
29569                 {
29570                     tag : 'b',
29571                     cls : 'roo-alert-title',
29572                     html : this.title
29573                 },
29574                 {
29575                     tag : 'span',
29576                     cls : 'roo-alert-text',
29577                     html : this.html
29578                 }
29579             ]
29580         };
29581         
29582         if(this.faicon){
29583             cfg.cn[0].cls += ' fa ' + this.faicon;
29584         }
29585         
29586         if(this.weight){
29587             cfg.cls += ' alert-' + this.weight;
29588         }
29589         
29590         return cfg;
29591     },
29592     
29593     initEvents: function() 
29594     {
29595         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29596     },
29597     
29598     setTitle : function(str)
29599     {
29600         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29601     },
29602     
29603     setText : function(str)
29604     {
29605         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29606     },
29607     
29608     setWeight : function(weight)
29609     {
29610         if(this.weight){
29611             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29612         }
29613         
29614         this.weight = weight;
29615         
29616         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29617     },
29618     
29619     setIcon : function(icon)
29620     {
29621         if(this.faicon){
29622             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29623         }
29624         
29625         this.faicon = icon;
29626         
29627         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29628     },
29629     
29630     hide: function() 
29631     {
29632         this.el.hide();   
29633     },
29634     
29635     show: function() 
29636     {  
29637         this.el.show();   
29638     }
29639     
29640 });
29641
29642  
29643 /*
29644 * Licence: LGPL
29645 */
29646
29647 /**
29648  * @class Roo.bootstrap.UploadCropbox
29649  * @extends Roo.bootstrap.Component
29650  * Bootstrap UploadCropbox class
29651  * @cfg {String} emptyText show when image has been loaded
29652  * @cfg {String} rotateNotify show when image too small to rotate
29653  * @cfg {Number} errorTimeout default 3000
29654  * @cfg {Number} minWidth default 300
29655  * @cfg {Number} minHeight default 300
29656  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29657  * @cfg {Boolean} isDocument (true|false) default false
29658  * @cfg {String} url action url
29659  * @cfg {String} paramName default 'imageUpload'
29660  * @cfg {String} method default POST
29661  * @cfg {Boolean} loadMask (true|false) default true
29662  * @cfg {Boolean} loadingText default 'Loading...'
29663  * 
29664  * @constructor
29665  * Create a new UploadCropbox
29666  * @param {Object} config The config object
29667  */
29668
29669 Roo.bootstrap.UploadCropbox = function(config){
29670     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29671     
29672     this.addEvents({
29673         /**
29674          * @event beforeselectfile
29675          * Fire before select file
29676          * @param {Roo.bootstrap.UploadCropbox} this
29677          */
29678         "beforeselectfile" : true,
29679         /**
29680          * @event initial
29681          * Fire after initEvent
29682          * @param {Roo.bootstrap.UploadCropbox} this
29683          */
29684         "initial" : true,
29685         /**
29686          * @event crop
29687          * Fire after initEvent
29688          * @param {Roo.bootstrap.UploadCropbox} this
29689          * @param {String} data
29690          */
29691         "crop" : true,
29692         /**
29693          * @event prepare
29694          * Fire when preparing the file data
29695          * @param {Roo.bootstrap.UploadCropbox} this
29696          * @param {Object} file
29697          */
29698         "prepare" : true,
29699         /**
29700          * @event exception
29701          * Fire when get exception
29702          * @param {Roo.bootstrap.UploadCropbox} this
29703          * @param {XMLHttpRequest} xhr
29704          */
29705         "exception" : true,
29706         /**
29707          * @event beforeloadcanvas
29708          * Fire before load the canvas
29709          * @param {Roo.bootstrap.UploadCropbox} this
29710          * @param {String} src
29711          */
29712         "beforeloadcanvas" : true,
29713         /**
29714          * @event trash
29715          * Fire when trash image
29716          * @param {Roo.bootstrap.UploadCropbox} this
29717          */
29718         "trash" : true,
29719         /**
29720          * @event download
29721          * Fire when download the image
29722          * @param {Roo.bootstrap.UploadCropbox} this
29723          */
29724         "download" : true,
29725         /**
29726          * @event footerbuttonclick
29727          * Fire when footerbuttonclick
29728          * @param {Roo.bootstrap.UploadCropbox} this
29729          * @param {String} type
29730          */
29731         "footerbuttonclick" : true,
29732         /**
29733          * @event resize
29734          * Fire when resize
29735          * @param {Roo.bootstrap.UploadCropbox} this
29736          */
29737         "resize" : true,
29738         /**
29739          * @event rotate
29740          * Fire when rotate the image
29741          * @param {Roo.bootstrap.UploadCropbox} this
29742          * @param {String} pos
29743          */
29744         "rotate" : true,
29745         /**
29746          * @event inspect
29747          * Fire when inspect the file
29748          * @param {Roo.bootstrap.UploadCropbox} this
29749          * @param {Object} file
29750          */
29751         "inspect" : true,
29752         /**
29753          * @event upload
29754          * Fire when xhr upload the file
29755          * @param {Roo.bootstrap.UploadCropbox} this
29756          * @param {Object} data
29757          */
29758         "upload" : true,
29759         /**
29760          * @event arrange
29761          * Fire when arrange the file data
29762          * @param {Roo.bootstrap.UploadCropbox} this
29763          * @param {Object} formData
29764          */
29765         "arrange" : true
29766     });
29767     
29768     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29769 };
29770
29771 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29772     
29773     emptyText : 'Click to upload image',
29774     rotateNotify : 'Image is too small to rotate',
29775     errorTimeout : 3000,
29776     scale : 0,
29777     baseScale : 1,
29778     rotate : 0,
29779     dragable : false,
29780     pinching : false,
29781     mouseX : 0,
29782     mouseY : 0,
29783     cropData : false,
29784     minWidth : 300,
29785     minHeight : 300,
29786     file : false,
29787     exif : {},
29788     baseRotate : 1,
29789     cropType : 'image/jpeg',
29790     buttons : false,
29791     canvasLoaded : false,
29792     isDocument : false,
29793     method : 'POST',
29794     paramName : 'imageUpload',
29795     loadMask : true,
29796     loadingText : 'Loading...',
29797     maskEl : false,
29798     
29799     getAutoCreate : function()
29800     {
29801         var cfg = {
29802             tag : 'div',
29803             cls : 'roo-upload-cropbox',
29804             cn : [
29805                 {
29806                     tag : 'input',
29807                     cls : 'roo-upload-cropbox-selector',
29808                     type : 'file'
29809                 },
29810                 {
29811                     tag : 'div',
29812                     cls : 'roo-upload-cropbox-body',
29813                     style : 'cursor:pointer',
29814                     cn : [
29815                         {
29816                             tag : 'div',
29817                             cls : 'roo-upload-cropbox-preview'
29818                         },
29819                         {
29820                             tag : 'div',
29821                             cls : 'roo-upload-cropbox-thumb'
29822                         },
29823                         {
29824                             tag : 'div',
29825                             cls : 'roo-upload-cropbox-empty-notify',
29826                             html : this.emptyText
29827                         },
29828                         {
29829                             tag : 'div',
29830                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29831                             html : this.rotateNotify
29832                         }
29833                     ]
29834                 },
29835                 {
29836                     tag : 'div',
29837                     cls : 'roo-upload-cropbox-footer',
29838                     cn : {
29839                         tag : 'div',
29840                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29841                         cn : []
29842                     }
29843                 }
29844             ]
29845         };
29846         
29847         return cfg;
29848     },
29849     
29850     onRender : function(ct, position)
29851     {
29852         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29853         
29854         if (this.buttons.length) {
29855             
29856             Roo.each(this.buttons, function(bb) {
29857                 
29858                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29859                 
29860                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29861                 
29862             }, this);
29863         }
29864         
29865         if(this.loadMask){
29866             this.maskEl = this.el;
29867         }
29868     },
29869     
29870     initEvents : function()
29871     {
29872         this.urlAPI = (window.createObjectURL && window) || 
29873                                 (window.URL && URL.revokeObjectURL && URL) || 
29874                                 (window.webkitURL && webkitURL);
29875                         
29876         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29877         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29878         
29879         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29880         this.selectorEl.hide();
29881         
29882         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29883         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29884         
29885         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29886         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29887         this.thumbEl.hide();
29888         
29889         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29890         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29891         
29892         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29893         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29894         this.errorEl.hide();
29895         
29896         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29897         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29898         this.footerEl.hide();
29899         
29900         this.setThumbBoxSize();
29901         
29902         this.bind();
29903         
29904         this.resize();
29905         
29906         this.fireEvent('initial', this);
29907     },
29908
29909     bind : function()
29910     {
29911         var _this = this;
29912         
29913         window.addEventListener("resize", function() { _this.resize(); } );
29914         
29915         this.bodyEl.on('click', this.beforeSelectFile, this);
29916         
29917         if(Roo.isTouch){
29918             this.bodyEl.on('touchstart', this.onTouchStart, this);
29919             this.bodyEl.on('touchmove', this.onTouchMove, this);
29920             this.bodyEl.on('touchend', this.onTouchEnd, this);
29921         }
29922         
29923         if(!Roo.isTouch){
29924             this.bodyEl.on('mousedown', this.onMouseDown, this);
29925             this.bodyEl.on('mousemove', this.onMouseMove, this);
29926             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29927             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29928             Roo.get(document).on('mouseup', this.onMouseUp, this);
29929         }
29930         
29931         this.selectorEl.on('change', this.onFileSelected, this);
29932     },
29933     
29934     reset : function()
29935     {    
29936         this.scale = 0;
29937         this.baseScale = 1;
29938         this.rotate = 0;
29939         this.baseRotate = 1;
29940         this.dragable = false;
29941         this.pinching = false;
29942         this.mouseX = 0;
29943         this.mouseY = 0;
29944         this.cropData = false;
29945         this.notifyEl.dom.innerHTML = this.emptyText;
29946         
29947         this.selectorEl.dom.value = '';
29948         
29949     },
29950     
29951     resize : function()
29952     {
29953         if(this.fireEvent('resize', this) != false){
29954             this.setThumbBoxPosition();
29955             this.setCanvasPosition();
29956         }
29957     },
29958     
29959     onFooterButtonClick : function(e, el, o, type)
29960     {
29961         switch (type) {
29962             case 'rotate-left' :
29963                 this.onRotateLeft(e);
29964                 break;
29965             case 'rotate-right' :
29966                 this.onRotateRight(e);
29967                 break;
29968             case 'picture' :
29969                 this.beforeSelectFile(e);
29970                 break;
29971             case 'trash' :
29972                 this.trash(e);
29973                 break;
29974             case 'crop' :
29975                 this.crop(e);
29976                 break;
29977             case 'download' :
29978                 this.download(e);
29979                 break;
29980             default :
29981                 break;
29982         }
29983         
29984         this.fireEvent('footerbuttonclick', this, type);
29985     },
29986     
29987     beforeSelectFile : function(e)
29988     {
29989         e.preventDefault();
29990         
29991         if(this.fireEvent('beforeselectfile', this) != false){
29992             this.selectorEl.dom.click();
29993         }
29994     },
29995     
29996     onFileSelected : function(e)
29997     {
29998         e.preventDefault();
29999         
30000         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30001             return;
30002         }
30003         
30004         var file = this.selectorEl.dom.files[0];
30005         
30006         if(this.fireEvent('inspect', this, file) != false){
30007             this.prepare(file);
30008         }
30009         
30010     },
30011     
30012     trash : function(e)
30013     {
30014         this.fireEvent('trash', this);
30015     },
30016     
30017     download : function(e)
30018     {
30019         this.fireEvent('download', this);
30020     },
30021     
30022     loadCanvas : function(src)
30023     {   
30024         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30025             
30026             this.reset();
30027             
30028             this.imageEl = document.createElement('img');
30029             
30030             var _this = this;
30031             
30032             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30033             
30034             this.imageEl.src = src;
30035         }
30036     },
30037     
30038     onLoadCanvas : function()
30039     {   
30040         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30041         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30042         
30043         this.bodyEl.un('click', this.beforeSelectFile, this);
30044         
30045         this.notifyEl.hide();
30046         this.thumbEl.show();
30047         this.footerEl.show();
30048         
30049         this.baseRotateLevel();
30050         
30051         if(this.isDocument){
30052             this.setThumbBoxSize();
30053         }
30054         
30055         this.setThumbBoxPosition();
30056         
30057         this.baseScaleLevel();
30058         
30059         this.draw();
30060         
30061         this.resize();
30062         
30063         this.canvasLoaded = true;
30064         
30065         if(this.loadMask){
30066             this.maskEl.unmask();
30067         }
30068         
30069     },
30070     
30071     setCanvasPosition : function()
30072     {   
30073         if(!this.canvasEl){
30074             return;
30075         }
30076         
30077         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30078         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30079         
30080         this.previewEl.setLeft(pw);
30081         this.previewEl.setTop(ph);
30082         
30083     },
30084     
30085     onMouseDown : function(e)
30086     {   
30087         e.stopEvent();
30088         
30089         this.dragable = true;
30090         this.pinching = false;
30091         
30092         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30093             this.dragable = false;
30094             return;
30095         }
30096         
30097         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30098         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30099         
30100     },
30101     
30102     onMouseMove : function(e)
30103     {   
30104         e.stopEvent();
30105         
30106         if(!this.canvasLoaded){
30107             return;
30108         }
30109         
30110         if (!this.dragable){
30111             return;
30112         }
30113         
30114         var minX = Math.ceil(this.thumbEl.getLeft(true));
30115         var minY = Math.ceil(this.thumbEl.getTop(true));
30116         
30117         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30118         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30119         
30120         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30121         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30122         
30123         x = x - this.mouseX;
30124         y = y - this.mouseY;
30125         
30126         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30127         var bgY = Math.ceil(y + this.previewEl.getTop(true));
30128         
30129         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30130         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30131         
30132         this.previewEl.setLeft(bgX);
30133         this.previewEl.setTop(bgY);
30134         
30135         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30136         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30137     },
30138     
30139     onMouseUp : function(e)
30140     {   
30141         e.stopEvent();
30142         
30143         this.dragable = false;
30144     },
30145     
30146     onMouseWheel : function(e)
30147     {   
30148         e.stopEvent();
30149         
30150         this.startScale = this.scale;
30151         
30152         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30153         
30154         if(!this.zoomable()){
30155             this.scale = this.startScale;
30156             return;
30157         }
30158         
30159         this.draw();
30160         
30161         return;
30162     },
30163     
30164     zoomable : function()
30165     {
30166         var minScale = this.thumbEl.getWidth() / this.minWidth;
30167         
30168         if(this.minWidth < this.minHeight){
30169             minScale = this.thumbEl.getHeight() / this.minHeight;
30170         }
30171         
30172         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30173         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30174         
30175         if(
30176                 this.isDocument &&
30177                 (this.rotate == 0 || this.rotate == 180) && 
30178                 (
30179                     width > this.imageEl.OriginWidth || 
30180                     height > this.imageEl.OriginHeight ||
30181                     (width < this.minWidth && height < this.minHeight)
30182                 )
30183         ){
30184             return false;
30185         }
30186         
30187         if(
30188                 this.isDocument &&
30189                 (this.rotate == 90 || this.rotate == 270) && 
30190                 (
30191                     width > this.imageEl.OriginWidth || 
30192                     height > this.imageEl.OriginHeight ||
30193                     (width < this.minHeight && height < this.minWidth)
30194                 )
30195         ){
30196             return false;
30197         }
30198         
30199         if(
30200                 !this.isDocument &&
30201                 (this.rotate == 0 || this.rotate == 180) && 
30202                 (
30203                     width < this.minWidth || 
30204                     width > this.imageEl.OriginWidth || 
30205                     height < this.minHeight || 
30206                     height > this.imageEl.OriginHeight
30207                 )
30208         ){
30209             return false;
30210         }
30211         
30212         if(
30213                 !this.isDocument &&
30214                 (this.rotate == 90 || this.rotate == 270) && 
30215                 (
30216                     width < this.minHeight || 
30217                     width > this.imageEl.OriginWidth || 
30218                     height < this.minWidth || 
30219                     height > this.imageEl.OriginHeight
30220                 )
30221         ){
30222             return false;
30223         }
30224         
30225         return true;
30226         
30227     },
30228     
30229     onRotateLeft : function(e)
30230     {   
30231         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30232             
30233             var minScale = this.thumbEl.getWidth() / this.minWidth;
30234             
30235             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30236             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30237             
30238             this.startScale = this.scale;
30239             
30240             while (this.getScaleLevel() < minScale){
30241             
30242                 this.scale = this.scale + 1;
30243                 
30244                 if(!this.zoomable()){
30245                     break;
30246                 }
30247                 
30248                 if(
30249                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30250                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30251                 ){
30252                     continue;
30253                 }
30254                 
30255                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30256
30257                 this.draw();
30258                 
30259                 return;
30260             }
30261             
30262             this.scale = this.startScale;
30263             
30264             this.onRotateFail();
30265             
30266             return false;
30267         }
30268         
30269         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30270
30271         if(this.isDocument){
30272             this.setThumbBoxSize();
30273             this.setThumbBoxPosition();
30274             this.setCanvasPosition();
30275         }
30276         
30277         this.draw();
30278         
30279         this.fireEvent('rotate', this, 'left');
30280         
30281     },
30282     
30283     onRotateRight : function(e)
30284     {
30285         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30286             
30287             var minScale = this.thumbEl.getWidth() / this.minWidth;
30288         
30289             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30290             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30291             
30292             this.startScale = this.scale;
30293             
30294             while (this.getScaleLevel() < minScale){
30295             
30296                 this.scale = this.scale + 1;
30297                 
30298                 if(!this.zoomable()){
30299                     break;
30300                 }
30301                 
30302                 if(
30303                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30304                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30305                 ){
30306                     continue;
30307                 }
30308                 
30309                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30310
30311                 this.draw();
30312                 
30313                 return;
30314             }
30315             
30316             this.scale = this.startScale;
30317             
30318             this.onRotateFail();
30319             
30320             return false;
30321         }
30322         
30323         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30324
30325         if(this.isDocument){
30326             this.setThumbBoxSize();
30327             this.setThumbBoxPosition();
30328             this.setCanvasPosition();
30329         }
30330         
30331         this.draw();
30332         
30333         this.fireEvent('rotate', this, 'right');
30334     },
30335     
30336     onRotateFail : function()
30337     {
30338         this.errorEl.show(true);
30339         
30340         var _this = this;
30341         
30342         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30343     },
30344     
30345     draw : function()
30346     {
30347         this.previewEl.dom.innerHTML = '';
30348         
30349         var canvasEl = document.createElement("canvas");
30350         
30351         var contextEl = canvasEl.getContext("2d");
30352         
30353         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30354         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30355         var center = this.imageEl.OriginWidth / 2;
30356         
30357         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30358             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30359             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30360             center = this.imageEl.OriginHeight / 2;
30361         }
30362         
30363         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30364         
30365         contextEl.translate(center, center);
30366         contextEl.rotate(this.rotate * Math.PI / 180);
30367
30368         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30369         
30370         this.canvasEl = document.createElement("canvas");
30371         
30372         this.contextEl = this.canvasEl.getContext("2d");
30373         
30374         switch (this.rotate) {
30375             case 0 :
30376                 
30377                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30378                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30379                 
30380                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30381                 
30382                 break;
30383             case 90 : 
30384                 
30385                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30386                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30387                 
30388                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30389                     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);
30390                     break;
30391                 }
30392                 
30393                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30394                 
30395                 break;
30396             case 180 :
30397                 
30398                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30399                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30400                 
30401                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30402                     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);
30403                     break;
30404                 }
30405                 
30406                 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);
30407                 
30408                 break;
30409             case 270 :
30410                 
30411                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30412                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30413         
30414                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30415                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30416                     break;
30417                 }
30418                 
30419                 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);
30420                 
30421                 break;
30422             default : 
30423                 break;
30424         }
30425         
30426         this.previewEl.appendChild(this.canvasEl);
30427         
30428         this.setCanvasPosition();
30429     },
30430     
30431     crop : function()
30432     {
30433         if(!this.canvasLoaded){
30434             return;
30435         }
30436         
30437         var imageCanvas = document.createElement("canvas");
30438         
30439         var imageContext = imageCanvas.getContext("2d");
30440         
30441         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30442         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30443         
30444         var center = imageCanvas.width / 2;
30445         
30446         imageContext.translate(center, center);
30447         
30448         imageContext.rotate(this.rotate * Math.PI / 180);
30449         
30450         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30451         
30452         var canvas = document.createElement("canvas");
30453         
30454         var context = canvas.getContext("2d");
30455                 
30456         canvas.width = this.minWidth;
30457         canvas.height = this.minHeight;
30458
30459         switch (this.rotate) {
30460             case 0 :
30461                 
30462                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30463                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30464                 
30465                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30466                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30467                 
30468                 var targetWidth = this.minWidth - 2 * x;
30469                 var targetHeight = this.minHeight - 2 * y;
30470                 
30471                 var scale = 1;
30472                 
30473                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30474                     scale = targetWidth / width;
30475                 }
30476                 
30477                 if(x > 0 && y == 0){
30478                     scale = targetHeight / height;
30479                 }
30480                 
30481                 if(x > 0 && y > 0){
30482                     scale = targetWidth / width;
30483                     
30484                     if(width < height){
30485                         scale = targetHeight / height;
30486                     }
30487                 }
30488                 
30489                 context.scale(scale, scale);
30490                 
30491                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30492                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30493
30494                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30495                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30496
30497                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30498                 
30499                 break;
30500             case 90 : 
30501                 
30502                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30503                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30504                 
30505                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30506                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30507                 
30508                 var targetWidth = this.minWidth - 2 * x;
30509                 var targetHeight = this.minHeight - 2 * y;
30510                 
30511                 var scale = 1;
30512                 
30513                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30514                     scale = targetWidth / width;
30515                 }
30516                 
30517                 if(x > 0 && y == 0){
30518                     scale = targetHeight / height;
30519                 }
30520                 
30521                 if(x > 0 && y > 0){
30522                     scale = targetWidth / width;
30523                     
30524                     if(width < height){
30525                         scale = targetHeight / height;
30526                     }
30527                 }
30528                 
30529                 context.scale(scale, scale);
30530                 
30531                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30532                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30533
30534                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30535                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30536                 
30537                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30538                 
30539                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30540                 
30541                 break;
30542             case 180 :
30543                 
30544                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30545                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30546                 
30547                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30548                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30549                 
30550                 var targetWidth = this.minWidth - 2 * x;
30551                 var targetHeight = this.minHeight - 2 * y;
30552                 
30553                 var scale = 1;
30554                 
30555                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30556                     scale = targetWidth / width;
30557                 }
30558                 
30559                 if(x > 0 && y == 0){
30560                     scale = targetHeight / height;
30561                 }
30562                 
30563                 if(x > 0 && y > 0){
30564                     scale = targetWidth / width;
30565                     
30566                     if(width < height){
30567                         scale = targetHeight / height;
30568                     }
30569                 }
30570                 
30571                 context.scale(scale, scale);
30572                 
30573                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30574                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30575
30576                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30577                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30578
30579                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30580                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30581                 
30582                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30583                 
30584                 break;
30585             case 270 :
30586                 
30587                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30588                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30589                 
30590                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30591                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30592                 
30593                 var targetWidth = this.minWidth - 2 * x;
30594                 var targetHeight = this.minHeight - 2 * y;
30595                 
30596                 var scale = 1;
30597                 
30598                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30599                     scale = targetWidth / width;
30600                 }
30601                 
30602                 if(x > 0 && y == 0){
30603                     scale = targetHeight / height;
30604                 }
30605                 
30606                 if(x > 0 && y > 0){
30607                     scale = targetWidth / width;
30608                     
30609                     if(width < height){
30610                         scale = targetHeight / height;
30611                     }
30612                 }
30613                 
30614                 context.scale(scale, scale);
30615                 
30616                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30617                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30618
30619                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30620                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30621                 
30622                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30623                 
30624                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30625                 
30626                 break;
30627             default : 
30628                 break;
30629         }
30630         
30631         this.cropData = canvas.toDataURL(this.cropType);
30632         
30633         if(this.fireEvent('crop', this, this.cropData) !== false){
30634             this.process(this.file, this.cropData);
30635         }
30636         
30637         return;
30638         
30639     },
30640     
30641     setThumbBoxSize : function()
30642     {
30643         var width, height;
30644         
30645         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30646             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30647             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30648             
30649             this.minWidth = width;
30650             this.minHeight = height;
30651             
30652             if(this.rotate == 90 || this.rotate == 270){
30653                 this.minWidth = height;
30654                 this.minHeight = width;
30655             }
30656         }
30657         
30658         height = 300;
30659         width = Math.ceil(this.minWidth * height / this.minHeight);
30660         
30661         if(this.minWidth > this.minHeight){
30662             width = 300;
30663             height = Math.ceil(this.minHeight * width / this.minWidth);
30664         }
30665         
30666         this.thumbEl.setStyle({
30667             width : width + 'px',
30668             height : height + 'px'
30669         });
30670
30671         return;
30672             
30673     },
30674     
30675     setThumbBoxPosition : function()
30676     {
30677         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30678         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30679         
30680         this.thumbEl.setLeft(x);
30681         this.thumbEl.setTop(y);
30682         
30683     },
30684     
30685     baseRotateLevel : function()
30686     {
30687         this.baseRotate = 1;
30688         
30689         if(
30690                 typeof(this.exif) != 'undefined' &&
30691                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30692                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30693         ){
30694             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30695         }
30696         
30697         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30698         
30699     },
30700     
30701     baseScaleLevel : function()
30702     {
30703         var width, height;
30704         
30705         if(this.isDocument){
30706             
30707             if(this.baseRotate == 6 || this.baseRotate == 8){
30708             
30709                 height = this.thumbEl.getHeight();
30710                 this.baseScale = height / this.imageEl.OriginWidth;
30711
30712                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30713                     width = this.thumbEl.getWidth();
30714                     this.baseScale = width / this.imageEl.OriginHeight;
30715                 }
30716
30717                 return;
30718             }
30719
30720             height = this.thumbEl.getHeight();
30721             this.baseScale = height / this.imageEl.OriginHeight;
30722
30723             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30724                 width = this.thumbEl.getWidth();
30725                 this.baseScale = width / this.imageEl.OriginWidth;
30726             }
30727
30728             return;
30729         }
30730         
30731         if(this.baseRotate == 6 || this.baseRotate == 8){
30732             
30733             width = this.thumbEl.getHeight();
30734             this.baseScale = width / this.imageEl.OriginHeight;
30735             
30736             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30737                 height = this.thumbEl.getWidth();
30738                 this.baseScale = height / this.imageEl.OriginHeight;
30739             }
30740             
30741             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30742                 height = this.thumbEl.getWidth();
30743                 this.baseScale = height / this.imageEl.OriginHeight;
30744                 
30745                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30746                     width = this.thumbEl.getHeight();
30747                     this.baseScale = width / this.imageEl.OriginWidth;
30748                 }
30749             }
30750             
30751             return;
30752         }
30753         
30754         width = this.thumbEl.getWidth();
30755         this.baseScale = width / this.imageEl.OriginWidth;
30756         
30757         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30758             height = this.thumbEl.getHeight();
30759             this.baseScale = height / this.imageEl.OriginHeight;
30760         }
30761         
30762         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30763             
30764             height = this.thumbEl.getHeight();
30765             this.baseScale = height / this.imageEl.OriginHeight;
30766             
30767             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30768                 width = this.thumbEl.getWidth();
30769                 this.baseScale = width / this.imageEl.OriginWidth;
30770             }
30771             
30772         }
30773         
30774         return;
30775     },
30776     
30777     getScaleLevel : function()
30778     {
30779         return this.baseScale * Math.pow(1.1, this.scale);
30780     },
30781     
30782     onTouchStart : function(e)
30783     {
30784         if(!this.canvasLoaded){
30785             this.beforeSelectFile(e);
30786             return;
30787         }
30788         
30789         var touches = e.browserEvent.touches;
30790         
30791         if(!touches){
30792             return;
30793         }
30794         
30795         if(touches.length == 1){
30796             this.onMouseDown(e);
30797             return;
30798         }
30799         
30800         if(touches.length != 2){
30801             return;
30802         }
30803         
30804         var coords = [];
30805         
30806         for(var i = 0, finger; finger = touches[i]; i++){
30807             coords.push(finger.pageX, finger.pageY);
30808         }
30809         
30810         var x = Math.pow(coords[0] - coords[2], 2);
30811         var y = Math.pow(coords[1] - coords[3], 2);
30812         
30813         this.startDistance = Math.sqrt(x + y);
30814         
30815         this.startScale = this.scale;
30816         
30817         this.pinching = true;
30818         this.dragable = false;
30819         
30820     },
30821     
30822     onTouchMove : function(e)
30823     {
30824         if(!this.pinching && !this.dragable){
30825             return;
30826         }
30827         
30828         var touches = e.browserEvent.touches;
30829         
30830         if(!touches){
30831             return;
30832         }
30833         
30834         if(this.dragable){
30835             this.onMouseMove(e);
30836             return;
30837         }
30838         
30839         var coords = [];
30840         
30841         for(var i = 0, finger; finger = touches[i]; i++){
30842             coords.push(finger.pageX, finger.pageY);
30843         }
30844         
30845         var x = Math.pow(coords[0] - coords[2], 2);
30846         var y = Math.pow(coords[1] - coords[3], 2);
30847         
30848         this.endDistance = Math.sqrt(x + y);
30849         
30850         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30851         
30852         if(!this.zoomable()){
30853             this.scale = this.startScale;
30854             return;
30855         }
30856         
30857         this.draw();
30858         
30859     },
30860     
30861     onTouchEnd : function(e)
30862     {
30863         this.pinching = false;
30864         this.dragable = false;
30865         
30866     },
30867     
30868     process : function(file, crop)
30869     {
30870         if(this.loadMask){
30871             this.maskEl.mask(this.loadingText);
30872         }
30873         
30874         this.xhr = new XMLHttpRequest();
30875         
30876         file.xhr = this.xhr;
30877
30878         this.xhr.open(this.method, this.url, true);
30879         
30880         var headers = {
30881             "Accept": "application/json",
30882             "Cache-Control": "no-cache",
30883             "X-Requested-With": "XMLHttpRequest"
30884         };
30885         
30886         for (var headerName in headers) {
30887             var headerValue = headers[headerName];
30888             if (headerValue) {
30889                 this.xhr.setRequestHeader(headerName, headerValue);
30890             }
30891         }
30892         
30893         var _this = this;
30894         
30895         this.xhr.onload = function()
30896         {
30897             _this.xhrOnLoad(_this.xhr);
30898         }
30899         
30900         this.xhr.onerror = function()
30901         {
30902             _this.xhrOnError(_this.xhr);
30903         }
30904         
30905         var formData = new FormData();
30906
30907         formData.append('returnHTML', 'NO');
30908         
30909         if(crop){
30910             formData.append('crop', crop);
30911         }
30912         
30913         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30914             formData.append(this.paramName, file, file.name);
30915         }
30916         
30917         if(typeof(file.filename) != 'undefined'){
30918             formData.append('filename', file.filename);
30919         }
30920         
30921         if(typeof(file.mimetype) != 'undefined'){
30922             formData.append('mimetype', file.mimetype);
30923         }
30924         
30925         if(this.fireEvent('arrange', this, formData) != false){
30926             this.xhr.send(formData);
30927         };
30928     },
30929     
30930     xhrOnLoad : function(xhr)
30931     {
30932         if(this.loadMask){
30933             this.maskEl.unmask();
30934         }
30935         
30936         if (xhr.readyState !== 4) {
30937             this.fireEvent('exception', this, xhr);
30938             return;
30939         }
30940
30941         var response = Roo.decode(xhr.responseText);
30942         
30943         if(!response.success){
30944             this.fireEvent('exception', this, xhr);
30945             return;
30946         }
30947         
30948         var response = Roo.decode(xhr.responseText);
30949         
30950         this.fireEvent('upload', this, response);
30951         
30952     },
30953     
30954     xhrOnError : function()
30955     {
30956         if(this.loadMask){
30957             this.maskEl.unmask();
30958         }
30959         
30960         Roo.log('xhr on error');
30961         
30962         var response = Roo.decode(xhr.responseText);
30963           
30964         Roo.log(response);
30965         
30966     },
30967     
30968     prepare : function(file)
30969     {   
30970         if(this.loadMask){
30971             this.maskEl.mask(this.loadingText);
30972         }
30973         
30974         this.file = false;
30975         this.exif = {};
30976         
30977         if(typeof(file) === 'string'){
30978             this.loadCanvas(file);
30979             return;
30980         }
30981         
30982         if(!file || !this.urlAPI){
30983             return;
30984         }
30985         
30986         this.file = file;
30987         this.cropType = file.type;
30988         
30989         var _this = this;
30990         
30991         if(this.fireEvent('prepare', this, this.file) != false){
30992             
30993             var reader = new FileReader();
30994             
30995             reader.onload = function (e) {
30996                 if (e.target.error) {
30997                     Roo.log(e.target.error);
30998                     return;
30999                 }
31000                 
31001                 var buffer = e.target.result,
31002                     dataView = new DataView(buffer),
31003                     offset = 2,
31004                     maxOffset = dataView.byteLength - 4,
31005                     markerBytes,
31006                     markerLength;
31007                 
31008                 if (dataView.getUint16(0) === 0xffd8) {
31009                     while (offset < maxOffset) {
31010                         markerBytes = dataView.getUint16(offset);
31011                         
31012                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31013                             markerLength = dataView.getUint16(offset + 2) + 2;
31014                             if (offset + markerLength > dataView.byteLength) {
31015                                 Roo.log('Invalid meta data: Invalid segment size.');
31016                                 break;
31017                             }
31018                             
31019                             if(markerBytes == 0xffe1){
31020                                 _this.parseExifData(
31021                                     dataView,
31022                                     offset,
31023                                     markerLength
31024                                 );
31025                             }
31026                             
31027                             offset += markerLength;
31028                             
31029                             continue;
31030                         }
31031                         
31032                         break;
31033                     }
31034                     
31035                 }
31036                 
31037                 var url = _this.urlAPI.createObjectURL(_this.file);
31038                 
31039                 _this.loadCanvas(url);
31040                 
31041                 return;
31042             }
31043             
31044             reader.readAsArrayBuffer(this.file);
31045             
31046         }
31047         
31048     },
31049     
31050     parseExifData : function(dataView, offset, length)
31051     {
31052         var tiffOffset = offset + 10,
31053             littleEndian,
31054             dirOffset;
31055     
31056         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31057             // No Exif data, might be XMP data instead
31058             return;
31059         }
31060         
31061         // Check for the ASCII code for "Exif" (0x45786966):
31062         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31063             // No Exif data, might be XMP data instead
31064             return;
31065         }
31066         if (tiffOffset + 8 > dataView.byteLength) {
31067             Roo.log('Invalid Exif data: Invalid segment size.');
31068             return;
31069         }
31070         // Check for the two null bytes:
31071         if (dataView.getUint16(offset + 8) !== 0x0000) {
31072             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31073             return;
31074         }
31075         // Check the byte alignment:
31076         switch (dataView.getUint16(tiffOffset)) {
31077         case 0x4949:
31078             littleEndian = true;
31079             break;
31080         case 0x4D4D:
31081             littleEndian = false;
31082             break;
31083         default:
31084             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31085             return;
31086         }
31087         // Check for the TIFF tag marker (0x002A):
31088         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31089             Roo.log('Invalid Exif data: Missing TIFF marker.');
31090             return;
31091         }
31092         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31093         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31094         
31095         this.parseExifTags(
31096             dataView,
31097             tiffOffset,
31098             tiffOffset + dirOffset,
31099             littleEndian
31100         );
31101     },
31102     
31103     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31104     {
31105         var tagsNumber,
31106             dirEndOffset,
31107             i;
31108         if (dirOffset + 6 > dataView.byteLength) {
31109             Roo.log('Invalid Exif data: Invalid directory offset.');
31110             return;
31111         }
31112         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31113         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31114         if (dirEndOffset + 4 > dataView.byteLength) {
31115             Roo.log('Invalid Exif data: Invalid directory size.');
31116             return;
31117         }
31118         for (i = 0; i < tagsNumber; i += 1) {
31119             this.parseExifTag(
31120                 dataView,
31121                 tiffOffset,
31122                 dirOffset + 2 + 12 * i, // tag offset
31123                 littleEndian
31124             );
31125         }
31126         // Return the offset to the next directory:
31127         return dataView.getUint32(dirEndOffset, littleEndian);
31128     },
31129     
31130     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31131     {
31132         var tag = dataView.getUint16(offset, littleEndian);
31133         
31134         this.exif[tag] = this.getExifValue(
31135             dataView,
31136             tiffOffset,
31137             offset,
31138             dataView.getUint16(offset + 2, littleEndian), // tag type
31139             dataView.getUint32(offset + 4, littleEndian), // tag length
31140             littleEndian
31141         );
31142     },
31143     
31144     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31145     {
31146         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31147             tagSize,
31148             dataOffset,
31149             values,
31150             i,
31151             str,
31152             c;
31153     
31154         if (!tagType) {
31155             Roo.log('Invalid Exif data: Invalid tag type.');
31156             return;
31157         }
31158         
31159         tagSize = tagType.size * length;
31160         // Determine if the value is contained in the dataOffset bytes,
31161         // or if the value at the dataOffset is a pointer to the actual data:
31162         dataOffset = tagSize > 4 ?
31163                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31164         if (dataOffset + tagSize > dataView.byteLength) {
31165             Roo.log('Invalid Exif data: Invalid data offset.');
31166             return;
31167         }
31168         if (length === 1) {
31169             return tagType.getValue(dataView, dataOffset, littleEndian);
31170         }
31171         values = [];
31172         for (i = 0; i < length; i += 1) {
31173             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31174         }
31175         
31176         if (tagType.ascii) {
31177             str = '';
31178             // Concatenate the chars:
31179             for (i = 0; i < values.length; i += 1) {
31180                 c = values[i];
31181                 // Ignore the terminating NULL byte(s):
31182                 if (c === '\u0000') {
31183                     break;
31184                 }
31185                 str += c;
31186             }
31187             return str;
31188         }
31189         return values;
31190     }
31191     
31192 });
31193
31194 Roo.apply(Roo.bootstrap.UploadCropbox, {
31195     tags : {
31196         'Orientation': 0x0112
31197     },
31198     
31199     Orientation: {
31200             1: 0, //'top-left',
31201 //            2: 'top-right',
31202             3: 180, //'bottom-right',
31203 //            4: 'bottom-left',
31204 //            5: 'left-top',
31205             6: 90, //'right-top',
31206 //            7: 'right-bottom',
31207             8: 270 //'left-bottom'
31208     },
31209     
31210     exifTagTypes : {
31211         // byte, 8-bit unsigned int:
31212         1: {
31213             getValue: function (dataView, dataOffset) {
31214                 return dataView.getUint8(dataOffset);
31215             },
31216             size: 1
31217         },
31218         // ascii, 8-bit byte:
31219         2: {
31220             getValue: function (dataView, dataOffset) {
31221                 return String.fromCharCode(dataView.getUint8(dataOffset));
31222             },
31223             size: 1,
31224             ascii: true
31225         },
31226         // short, 16 bit int:
31227         3: {
31228             getValue: function (dataView, dataOffset, littleEndian) {
31229                 return dataView.getUint16(dataOffset, littleEndian);
31230             },
31231             size: 2
31232         },
31233         // long, 32 bit int:
31234         4: {
31235             getValue: function (dataView, dataOffset, littleEndian) {
31236                 return dataView.getUint32(dataOffset, littleEndian);
31237             },
31238             size: 4
31239         },
31240         // rational = two long values, first is numerator, second is denominator:
31241         5: {
31242             getValue: function (dataView, dataOffset, littleEndian) {
31243                 return dataView.getUint32(dataOffset, littleEndian) /
31244                     dataView.getUint32(dataOffset + 4, littleEndian);
31245             },
31246             size: 8
31247         },
31248         // slong, 32 bit signed int:
31249         9: {
31250             getValue: function (dataView, dataOffset, littleEndian) {
31251                 return dataView.getInt32(dataOffset, littleEndian);
31252             },
31253             size: 4
31254         },
31255         // srational, two slongs, first is numerator, second is denominator:
31256         10: {
31257             getValue: function (dataView, dataOffset, littleEndian) {
31258                 return dataView.getInt32(dataOffset, littleEndian) /
31259                     dataView.getInt32(dataOffset + 4, littleEndian);
31260             },
31261             size: 8
31262         }
31263     },
31264     
31265     footer : {
31266         STANDARD : [
31267             {
31268                 tag : 'div',
31269                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31270                 action : 'rotate-left',
31271                 cn : [
31272                     {
31273                         tag : 'button',
31274                         cls : 'btn btn-default',
31275                         html : '<i class="fa fa-undo"></i>'
31276                     }
31277                 ]
31278             },
31279             {
31280                 tag : 'div',
31281                 cls : 'btn-group roo-upload-cropbox-picture',
31282                 action : 'picture',
31283                 cn : [
31284                     {
31285                         tag : 'button',
31286                         cls : 'btn btn-default',
31287                         html : '<i class="fa fa-picture-o"></i>'
31288                     }
31289                 ]
31290             },
31291             {
31292                 tag : 'div',
31293                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31294                 action : 'rotate-right',
31295                 cn : [
31296                     {
31297                         tag : 'button',
31298                         cls : 'btn btn-default',
31299                         html : '<i class="fa fa-repeat"></i>'
31300                     }
31301                 ]
31302             }
31303         ],
31304         DOCUMENT : [
31305             {
31306                 tag : 'div',
31307                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31308                 action : 'rotate-left',
31309                 cn : [
31310                     {
31311                         tag : 'button',
31312                         cls : 'btn btn-default',
31313                         html : '<i class="fa fa-undo"></i>'
31314                     }
31315                 ]
31316             },
31317             {
31318                 tag : 'div',
31319                 cls : 'btn-group roo-upload-cropbox-download',
31320                 action : 'download',
31321                 cn : [
31322                     {
31323                         tag : 'button',
31324                         cls : 'btn btn-default',
31325                         html : '<i class="fa fa-download"></i>'
31326                     }
31327                 ]
31328             },
31329             {
31330                 tag : 'div',
31331                 cls : 'btn-group roo-upload-cropbox-crop',
31332                 action : 'crop',
31333                 cn : [
31334                     {
31335                         tag : 'button',
31336                         cls : 'btn btn-default',
31337                         html : '<i class="fa fa-crop"></i>'
31338                     }
31339                 ]
31340             },
31341             {
31342                 tag : 'div',
31343                 cls : 'btn-group roo-upload-cropbox-trash',
31344                 action : 'trash',
31345                 cn : [
31346                     {
31347                         tag : 'button',
31348                         cls : 'btn btn-default',
31349                         html : '<i class="fa fa-trash"></i>'
31350                     }
31351                 ]
31352             },
31353             {
31354                 tag : 'div',
31355                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31356                 action : 'rotate-right',
31357                 cn : [
31358                     {
31359                         tag : 'button',
31360                         cls : 'btn btn-default',
31361                         html : '<i class="fa fa-repeat"></i>'
31362                     }
31363                 ]
31364             }
31365         ],
31366         ROTATOR : [
31367             {
31368                 tag : 'div',
31369                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31370                 action : 'rotate-left',
31371                 cn : [
31372                     {
31373                         tag : 'button',
31374                         cls : 'btn btn-default',
31375                         html : '<i class="fa fa-undo"></i>'
31376                     }
31377                 ]
31378             },
31379             {
31380                 tag : 'div',
31381                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31382                 action : 'rotate-right',
31383                 cn : [
31384                     {
31385                         tag : 'button',
31386                         cls : 'btn btn-default',
31387                         html : '<i class="fa fa-repeat"></i>'
31388                     }
31389                 ]
31390             }
31391         ]
31392     }
31393 });
31394
31395 /*
31396 * Licence: LGPL
31397 */
31398
31399 /**
31400  * @class Roo.bootstrap.DocumentManager
31401  * @extends Roo.bootstrap.Component
31402  * Bootstrap DocumentManager class
31403  * @cfg {String} paramName default 'imageUpload'
31404  * @cfg {String} toolTipName default 'filename'
31405  * @cfg {String} method default POST
31406  * @cfg {String} url action url
31407  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31408  * @cfg {Boolean} multiple multiple upload default true
31409  * @cfg {Number} thumbSize default 300
31410  * @cfg {String} fieldLabel
31411  * @cfg {Number} labelWidth default 4
31412  * @cfg {String} labelAlign (left|top) default left
31413  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31414 * @cfg {Number} labellg set the width of label (1-12)
31415  * @cfg {Number} labelmd set the width of label (1-12)
31416  * @cfg {Number} labelsm set the width of label (1-12)
31417  * @cfg {Number} labelxs set the width of label (1-12)
31418  * 
31419  * @constructor
31420  * Create a new DocumentManager
31421  * @param {Object} config The config object
31422  */
31423
31424 Roo.bootstrap.DocumentManager = function(config){
31425     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31426     
31427     this.files = [];
31428     this.delegates = [];
31429     
31430     this.addEvents({
31431         /**
31432          * @event initial
31433          * Fire when initial the DocumentManager
31434          * @param {Roo.bootstrap.DocumentManager} this
31435          */
31436         "initial" : true,
31437         /**
31438          * @event inspect
31439          * inspect selected file
31440          * @param {Roo.bootstrap.DocumentManager} this
31441          * @param {File} file
31442          */
31443         "inspect" : true,
31444         /**
31445          * @event exception
31446          * Fire when xhr load exception
31447          * @param {Roo.bootstrap.DocumentManager} this
31448          * @param {XMLHttpRequest} xhr
31449          */
31450         "exception" : true,
31451         /**
31452          * @event afterupload
31453          * Fire when xhr load exception
31454          * @param {Roo.bootstrap.DocumentManager} this
31455          * @param {XMLHttpRequest} xhr
31456          */
31457         "afterupload" : true,
31458         /**
31459          * @event prepare
31460          * prepare the form data
31461          * @param {Roo.bootstrap.DocumentManager} this
31462          * @param {Object} formData
31463          */
31464         "prepare" : true,
31465         /**
31466          * @event remove
31467          * Fire when remove the file
31468          * @param {Roo.bootstrap.DocumentManager} this
31469          * @param {Object} file
31470          */
31471         "remove" : true,
31472         /**
31473          * @event refresh
31474          * Fire after refresh the file
31475          * @param {Roo.bootstrap.DocumentManager} this
31476          */
31477         "refresh" : true,
31478         /**
31479          * @event click
31480          * Fire after click the image
31481          * @param {Roo.bootstrap.DocumentManager} this
31482          * @param {Object} file
31483          */
31484         "click" : true,
31485         /**
31486          * @event edit
31487          * Fire when upload a image and editable set to true
31488          * @param {Roo.bootstrap.DocumentManager} this
31489          * @param {Object} file
31490          */
31491         "edit" : true,
31492         /**
31493          * @event beforeselectfile
31494          * Fire before select file
31495          * @param {Roo.bootstrap.DocumentManager} this
31496          */
31497         "beforeselectfile" : true,
31498         /**
31499          * @event process
31500          * Fire before process file
31501          * @param {Roo.bootstrap.DocumentManager} this
31502          * @param {Object} file
31503          */
31504         "process" : true,
31505         /**
31506          * @event previewrendered
31507          * Fire when preview rendered
31508          * @param {Roo.bootstrap.DocumentManager} this
31509          * @param {Object} file
31510          */
31511         "previewrendered" : true,
31512         /**
31513          */
31514         "previewResize" : true
31515         
31516     });
31517 };
31518
31519 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31520     
31521     boxes : 0,
31522     inputName : '',
31523     thumbSize : 300,
31524     multiple : true,
31525     files : false,
31526     method : 'POST',
31527     url : '',
31528     paramName : 'imageUpload',
31529     toolTipName : 'filename',
31530     fieldLabel : '',
31531     labelWidth : 4,
31532     labelAlign : 'left',
31533     editable : true,
31534     delegates : false,
31535     xhr : false, 
31536     
31537     labellg : 0,
31538     labelmd : 0,
31539     labelsm : 0,
31540     labelxs : 0,
31541     
31542     getAutoCreate : function()
31543     {   
31544         var managerWidget = {
31545             tag : 'div',
31546             cls : 'roo-document-manager',
31547             cn : [
31548                 {
31549                     tag : 'input',
31550                     cls : 'roo-document-manager-selector',
31551                     type : 'file'
31552                 },
31553                 {
31554                     tag : 'div',
31555                     cls : 'roo-document-manager-uploader',
31556                     cn : [
31557                         {
31558                             tag : 'div',
31559                             cls : 'roo-document-manager-upload-btn',
31560                             html : '<i class="fa fa-plus"></i>'
31561                         }
31562                     ]
31563                     
31564                 }
31565             ]
31566         };
31567         
31568         var content = [
31569             {
31570                 tag : 'div',
31571                 cls : 'column col-md-12',
31572                 cn : managerWidget
31573             }
31574         ];
31575         
31576         if(this.fieldLabel.length){
31577             
31578             content = [
31579                 {
31580                     tag : 'div',
31581                     cls : 'column col-md-12',
31582                     html : this.fieldLabel
31583                 },
31584                 {
31585                     tag : 'div',
31586                     cls : 'column col-md-12',
31587                     cn : managerWidget
31588                 }
31589             ];
31590
31591             if(this.labelAlign == 'left'){
31592                 content = [
31593                     {
31594                         tag : 'div',
31595                         cls : 'column',
31596                         html : this.fieldLabel
31597                     },
31598                     {
31599                         tag : 'div',
31600                         cls : 'column',
31601                         cn : managerWidget
31602                     }
31603                 ];
31604                 
31605                 if(this.labelWidth > 12){
31606                     content[0].style = "width: " + this.labelWidth + 'px';
31607                 }
31608
31609                 if(this.labelWidth < 13 && this.labelmd == 0){
31610                     this.labelmd = this.labelWidth;
31611                 }
31612
31613                 if(this.labellg > 0){
31614                     content[0].cls += ' col-lg-' + this.labellg;
31615                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31616                 }
31617
31618                 if(this.labelmd > 0){
31619                     content[0].cls += ' col-md-' + this.labelmd;
31620                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31621                 }
31622
31623                 if(this.labelsm > 0){
31624                     content[0].cls += ' col-sm-' + this.labelsm;
31625                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31626                 }
31627
31628                 if(this.labelxs > 0){
31629                     content[0].cls += ' col-xs-' + this.labelxs;
31630                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31631                 }
31632                 
31633             }
31634         }
31635         
31636         var cfg = {
31637             tag : 'div',
31638             cls : 'row clearfix',
31639             cn : content
31640         };
31641         
31642         return cfg;
31643         
31644     },
31645     
31646     initEvents : function()
31647     {
31648         this.managerEl = this.el.select('.roo-document-manager', true).first();
31649         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31650         
31651         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31652         this.selectorEl.hide();
31653         
31654         if(this.multiple){
31655             this.selectorEl.attr('multiple', 'multiple');
31656         }
31657         
31658         this.selectorEl.on('change', this.onFileSelected, this);
31659         
31660         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31661         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31662         
31663         this.uploader.on('click', this.onUploaderClick, this);
31664         
31665         this.renderProgressDialog();
31666         
31667         var _this = this;
31668         
31669         window.addEventListener("resize", function() { _this.refresh(); } );
31670         
31671         this.fireEvent('initial', this);
31672     },
31673     
31674     renderProgressDialog : function()
31675     {
31676         var _this = this;
31677         
31678         this.progressDialog = new Roo.bootstrap.Modal({
31679             cls : 'roo-document-manager-progress-dialog',
31680             allow_close : false,
31681             animate : false,
31682             title : '',
31683             buttons : [
31684                 {
31685                     name  :'cancel',
31686                     weight : 'danger',
31687                     html : 'Cancel'
31688                 }
31689             ], 
31690             listeners : { 
31691                 btnclick : function() {
31692                     _this.uploadCancel();
31693                     this.hide();
31694                 }
31695             }
31696         });
31697          
31698         this.progressDialog.render(Roo.get(document.body));
31699          
31700         this.progress = new Roo.bootstrap.Progress({
31701             cls : 'roo-document-manager-progress',
31702             active : true,
31703             striped : true
31704         });
31705         
31706         this.progress.render(this.progressDialog.getChildContainer());
31707         
31708         this.progressBar = new Roo.bootstrap.ProgressBar({
31709             cls : 'roo-document-manager-progress-bar',
31710             aria_valuenow : 0,
31711             aria_valuemin : 0,
31712             aria_valuemax : 12,
31713             panel : 'success'
31714         });
31715         
31716         this.progressBar.render(this.progress.getChildContainer());
31717     },
31718     
31719     onUploaderClick : function(e)
31720     {
31721         e.preventDefault();
31722      
31723         if(this.fireEvent('beforeselectfile', this) != false){
31724             this.selectorEl.dom.click();
31725         }
31726         
31727     },
31728     
31729     onFileSelected : function(e)
31730     {
31731         e.preventDefault();
31732         
31733         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31734             return;
31735         }
31736         
31737         Roo.each(this.selectorEl.dom.files, function(file){
31738             if(this.fireEvent('inspect', this, file) != false){
31739                 this.files.push(file);
31740             }
31741         }, this);
31742         
31743         this.queue();
31744         
31745     },
31746     
31747     queue : function()
31748     {
31749         this.selectorEl.dom.value = '';
31750         
31751         if(!this.files || !this.files.length){
31752             return;
31753         }
31754         
31755         if(this.boxes > 0 && this.files.length > this.boxes){
31756             this.files = this.files.slice(0, this.boxes);
31757         }
31758         
31759         this.uploader.show();
31760         
31761         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31762             this.uploader.hide();
31763         }
31764         
31765         var _this = this;
31766         
31767         var files = [];
31768         
31769         var docs = [];
31770         
31771         Roo.each(this.files, function(file){
31772             
31773             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31774                 var f = this.renderPreview(file);
31775                 files.push(f);
31776                 return;
31777             }
31778             
31779             if(file.type.indexOf('image') != -1){
31780                 this.delegates.push(
31781                     (function(){
31782                         _this.process(file);
31783                     }).createDelegate(this)
31784                 );
31785         
31786                 return;
31787             }
31788             
31789             docs.push(
31790                 (function(){
31791                     _this.process(file);
31792                 }).createDelegate(this)
31793             );
31794             
31795         }, this);
31796         
31797         this.files = files;
31798         
31799         this.delegates = this.delegates.concat(docs);
31800         
31801         if(!this.delegates.length){
31802             this.refresh();
31803             return;
31804         }
31805         
31806         this.progressBar.aria_valuemax = this.delegates.length;
31807         
31808         this.arrange();
31809         
31810         return;
31811     },
31812     
31813     arrange : function()
31814     {
31815         if(!this.delegates.length){
31816             this.progressDialog.hide();
31817             this.refresh();
31818             return;
31819         }
31820         
31821         var delegate = this.delegates.shift();
31822         
31823         this.progressDialog.show();
31824         
31825         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31826         
31827         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31828         
31829         delegate();
31830     },
31831     
31832     refresh : function()
31833     {
31834         this.uploader.show();
31835         
31836         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31837             this.uploader.hide();
31838         }
31839         
31840         Roo.isTouch ? this.closable(false) : this.closable(true);
31841         
31842         this.fireEvent('refresh', this);
31843     },
31844     
31845     onRemove : function(e, el, o)
31846     {
31847         e.preventDefault();
31848         
31849         this.fireEvent('remove', this, o);
31850         
31851     },
31852     
31853     remove : function(o)
31854     {
31855         var files = [];
31856         
31857         Roo.each(this.files, function(file){
31858             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31859                 files.push(file);
31860                 return;
31861             }
31862
31863             o.target.remove();
31864
31865         }, this);
31866         
31867         this.files = files;
31868         
31869         this.refresh();
31870     },
31871     
31872     clear : function()
31873     {
31874         Roo.each(this.files, function(file){
31875             if(!file.target){
31876                 return;
31877             }
31878             
31879             file.target.remove();
31880
31881         }, this);
31882         
31883         this.files = [];
31884         
31885         this.refresh();
31886     },
31887     
31888     onClick : function(e, el, o)
31889     {
31890         e.preventDefault();
31891         
31892         this.fireEvent('click', this, o);
31893         
31894     },
31895     
31896     closable : function(closable)
31897     {
31898         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31899             
31900             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31901             
31902             if(closable){
31903                 el.show();
31904                 return;
31905             }
31906             
31907             el.hide();
31908             
31909         }, this);
31910     },
31911     
31912     xhrOnLoad : function(xhr)
31913     {
31914         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31915             el.remove();
31916         }, this);
31917         
31918         if (xhr.readyState !== 4) {
31919             this.arrange();
31920             this.fireEvent('exception', this, xhr);
31921             return;
31922         }
31923
31924         var response = Roo.decode(xhr.responseText);
31925         
31926         if(!response.success){
31927             this.arrange();
31928             this.fireEvent('exception', this, xhr);
31929             return;
31930         }
31931         
31932         var file = this.renderPreview(response.data);
31933         
31934         this.files.push(file);
31935         
31936         this.arrange();
31937         
31938         this.fireEvent('afterupload', this, xhr);
31939         
31940     },
31941     
31942     xhrOnError : function(xhr)
31943     {
31944         Roo.log('xhr on error');
31945         
31946         var response = Roo.decode(xhr.responseText);
31947           
31948         Roo.log(response);
31949         
31950         this.arrange();
31951     },
31952     
31953     process : function(file)
31954     {
31955         if(this.fireEvent('process', this, file) !== false){
31956             if(this.editable && file.type.indexOf('image') != -1){
31957                 this.fireEvent('edit', this, file);
31958                 return;
31959             }
31960
31961             this.uploadStart(file, false);
31962
31963             return;
31964         }
31965         
31966     },
31967     
31968     uploadStart : function(file, crop)
31969     {
31970         this.xhr = new XMLHttpRequest();
31971         
31972         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31973             this.arrange();
31974             return;
31975         }
31976         
31977         file.xhr = this.xhr;
31978             
31979         this.managerEl.createChild({
31980             tag : 'div',
31981             cls : 'roo-document-manager-loading',
31982             cn : [
31983                 {
31984                     tag : 'div',
31985                     tooltip : file.name,
31986                     cls : 'roo-document-manager-thumb',
31987                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31988                 }
31989             ]
31990
31991         });
31992
31993         this.xhr.open(this.method, this.url, true);
31994         
31995         var headers = {
31996             "Accept": "application/json",
31997             "Cache-Control": "no-cache",
31998             "X-Requested-With": "XMLHttpRequest"
31999         };
32000         
32001         for (var headerName in headers) {
32002             var headerValue = headers[headerName];
32003             if (headerValue) {
32004                 this.xhr.setRequestHeader(headerName, headerValue);
32005             }
32006         }
32007         
32008         var _this = this;
32009         
32010         this.xhr.onload = function()
32011         {
32012             _this.xhrOnLoad(_this.xhr);
32013         }
32014         
32015         this.xhr.onerror = function()
32016         {
32017             _this.xhrOnError(_this.xhr);
32018         }
32019         
32020         var formData = new FormData();
32021
32022         formData.append('returnHTML', 'NO');
32023         
32024         if(crop){
32025             formData.append('crop', crop);
32026         }
32027         
32028         formData.append(this.paramName, file, file.name);
32029         
32030         var options = {
32031             file : file, 
32032             manually : false
32033         };
32034         
32035         if(this.fireEvent('prepare', this, formData, options) != false){
32036             
32037             if(options.manually){
32038                 return;
32039             }
32040             
32041             this.xhr.send(formData);
32042             return;
32043         };
32044         
32045         this.uploadCancel();
32046     },
32047     
32048     uploadCancel : function()
32049     {
32050         if (this.xhr) {
32051             this.xhr.abort();
32052         }
32053         
32054         this.delegates = [];
32055         
32056         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32057             el.remove();
32058         }, this);
32059         
32060         this.arrange();
32061     },
32062     
32063     renderPreview : function(file)
32064     {
32065         if(typeof(file.target) != 'undefined' && file.target){
32066             return file;
32067         }
32068         
32069         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32070         
32071         var previewEl = this.managerEl.createChild({
32072             tag : 'div',
32073             cls : 'roo-document-manager-preview',
32074             cn : [
32075                 {
32076                     tag : 'div',
32077                     tooltip : file[this.toolTipName],
32078                     cls : 'roo-document-manager-thumb',
32079                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32080                 },
32081                 {
32082                     tag : 'button',
32083                     cls : 'close',
32084                     html : '<i class="fa fa-times-circle"></i>'
32085                 }
32086             ]
32087         });
32088
32089         var close = previewEl.select('button.close', true).first();
32090
32091         close.on('click', this.onRemove, this, file);
32092
32093         file.target = previewEl;
32094
32095         var image = previewEl.select('img', true).first();
32096         
32097         var _this = this;
32098         
32099         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32100         
32101         image.on('click', this.onClick, this, file);
32102         
32103         this.fireEvent('previewrendered', this, file);
32104         
32105         return file;
32106         
32107     },
32108     
32109     onPreviewLoad : function(file, image)
32110     {
32111         if(typeof(file.target) == 'undefined' || !file.target){
32112             return;
32113         }
32114         
32115         var width = image.dom.naturalWidth || image.dom.width;
32116         var height = image.dom.naturalHeight || image.dom.height;
32117         
32118         if(!this.previewResize) {
32119             return;
32120         }
32121         
32122         if(width > height){
32123             file.target.addClass('wide');
32124             return;
32125         }
32126         
32127         file.target.addClass('tall');
32128         return;
32129         
32130     },
32131     
32132     uploadFromSource : function(file, crop)
32133     {
32134         this.xhr = new XMLHttpRequest();
32135         
32136         this.managerEl.createChild({
32137             tag : 'div',
32138             cls : 'roo-document-manager-loading',
32139             cn : [
32140                 {
32141                     tag : 'div',
32142                     tooltip : file.name,
32143                     cls : 'roo-document-manager-thumb',
32144                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32145                 }
32146             ]
32147
32148         });
32149
32150         this.xhr.open(this.method, this.url, true);
32151         
32152         var headers = {
32153             "Accept": "application/json",
32154             "Cache-Control": "no-cache",
32155             "X-Requested-With": "XMLHttpRequest"
32156         };
32157         
32158         for (var headerName in headers) {
32159             var headerValue = headers[headerName];
32160             if (headerValue) {
32161                 this.xhr.setRequestHeader(headerName, headerValue);
32162             }
32163         }
32164         
32165         var _this = this;
32166         
32167         this.xhr.onload = function()
32168         {
32169             _this.xhrOnLoad(_this.xhr);
32170         }
32171         
32172         this.xhr.onerror = function()
32173         {
32174             _this.xhrOnError(_this.xhr);
32175         }
32176         
32177         var formData = new FormData();
32178
32179         formData.append('returnHTML', 'NO');
32180         
32181         formData.append('crop', crop);
32182         
32183         if(typeof(file.filename) != 'undefined'){
32184             formData.append('filename', file.filename);
32185         }
32186         
32187         if(typeof(file.mimetype) != 'undefined'){
32188             formData.append('mimetype', file.mimetype);
32189         }
32190         
32191         Roo.log(formData);
32192         
32193         if(this.fireEvent('prepare', this, formData) != false){
32194             this.xhr.send(formData);
32195         };
32196     }
32197 });
32198
32199 /*
32200 * Licence: LGPL
32201 */
32202
32203 /**
32204  * @class Roo.bootstrap.DocumentViewer
32205  * @extends Roo.bootstrap.Component
32206  * Bootstrap DocumentViewer class
32207  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32208  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32209  * 
32210  * @constructor
32211  * Create a new DocumentViewer
32212  * @param {Object} config The config object
32213  */
32214
32215 Roo.bootstrap.DocumentViewer = function(config){
32216     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32217     
32218     this.addEvents({
32219         /**
32220          * @event initial
32221          * Fire after initEvent
32222          * @param {Roo.bootstrap.DocumentViewer} this
32223          */
32224         "initial" : true,
32225         /**
32226          * @event click
32227          * Fire after click
32228          * @param {Roo.bootstrap.DocumentViewer} this
32229          */
32230         "click" : true,
32231         /**
32232          * @event download
32233          * Fire after download button
32234          * @param {Roo.bootstrap.DocumentViewer} this
32235          */
32236         "download" : true,
32237         /**
32238          * @event trash
32239          * Fire after trash button
32240          * @param {Roo.bootstrap.DocumentViewer} this
32241          */
32242         "trash" : true
32243         
32244     });
32245 };
32246
32247 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32248     
32249     showDownload : true,
32250     
32251     showTrash : true,
32252     
32253     getAutoCreate : function()
32254     {
32255         var cfg = {
32256             tag : 'div',
32257             cls : 'roo-document-viewer',
32258             cn : [
32259                 {
32260                     tag : 'div',
32261                     cls : 'roo-document-viewer-body',
32262                     cn : [
32263                         {
32264                             tag : 'div',
32265                             cls : 'roo-document-viewer-thumb',
32266                             cn : [
32267                                 {
32268                                     tag : 'img',
32269                                     cls : 'roo-document-viewer-image'
32270                                 }
32271                             ]
32272                         }
32273                     ]
32274                 },
32275                 {
32276                     tag : 'div',
32277                     cls : 'roo-document-viewer-footer',
32278                     cn : {
32279                         tag : 'div',
32280                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32281                         cn : [
32282                             {
32283                                 tag : 'div',
32284                                 cls : 'btn-group roo-document-viewer-download',
32285                                 cn : [
32286                                     {
32287                                         tag : 'button',
32288                                         cls : 'btn btn-default',
32289                                         html : '<i class="fa fa-download"></i>'
32290                                     }
32291                                 ]
32292                             },
32293                             {
32294                                 tag : 'div',
32295                                 cls : 'btn-group roo-document-viewer-trash',
32296                                 cn : [
32297                                     {
32298                                         tag : 'button',
32299                                         cls : 'btn btn-default',
32300                                         html : '<i class="fa fa-trash"></i>'
32301                                     }
32302                                 ]
32303                             }
32304                         ]
32305                     }
32306                 }
32307             ]
32308         };
32309         
32310         return cfg;
32311     },
32312     
32313     initEvents : function()
32314     {
32315         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32316         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32317         
32318         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32319         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32320         
32321         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32322         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32323         
32324         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32325         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32326         
32327         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32328         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32329         
32330         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32331         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32332         
32333         this.bodyEl.on('click', this.onClick, this);
32334         this.downloadBtn.on('click', this.onDownload, this);
32335         this.trashBtn.on('click', this.onTrash, this);
32336         
32337         this.downloadBtn.hide();
32338         this.trashBtn.hide();
32339         
32340         if(this.showDownload){
32341             this.downloadBtn.show();
32342         }
32343         
32344         if(this.showTrash){
32345             this.trashBtn.show();
32346         }
32347         
32348         if(!this.showDownload && !this.showTrash) {
32349             this.footerEl.hide();
32350         }
32351         
32352     },
32353     
32354     initial : function()
32355     {
32356         this.fireEvent('initial', this);
32357         
32358     },
32359     
32360     onClick : function(e)
32361     {
32362         e.preventDefault();
32363         
32364         this.fireEvent('click', this);
32365     },
32366     
32367     onDownload : function(e)
32368     {
32369         e.preventDefault();
32370         
32371         this.fireEvent('download', this);
32372     },
32373     
32374     onTrash : function(e)
32375     {
32376         e.preventDefault();
32377         
32378         this.fireEvent('trash', this);
32379     }
32380     
32381 });
32382 /*
32383  * - LGPL
32384  *
32385  * nav progress bar
32386  * 
32387  */
32388
32389 /**
32390  * @class Roo.bootstrap.NavProgressBar
32391  * @extends Roo.bootstrap.Component
32392  * Bootstrap NavProgressBar class
32393  * 
32394  * @constructor
32395  * Create a new nav progress bar
32396  * @param {Object} config The config object
32397  */
32398
32399 Roo.bootstrap.NavProgressBar = function(config){
32400     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32401
32402     this.bullets = this.bullets || [];
32403    
32404 //    Roo.bootstrap.NavProgressBar.register(this);
32405      this.addEvents({
32406         /**
32407              * @event changed
32408              * Fires when the active item changes
32409              * @param {Roo.bootstrap.NavProgressBar} this
32410              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32411              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32412          */
32413         'changed': true
32414      });
32415     
32416 };
32417
32418 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32419     
32420     bullets : [],
32421     barItems : [],
32422     
32423     getAutoCreate : function()
32424     {
32425         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32426         
32427         cfg = {
32428             tag : 'div',
32429             cls : 'roo-navigation-bar-group',
32430             cn : [
32431                 {
32432                     tag : 'div',
32433                     cls : 'roo-navigation-top-bar'
32434                 },
32435                 {
32436                     tag : 'div',
32437                     cls : 'roo-navigation-bullets-bar',
32438                     cn : [
32439                         {
32440                             tag : 'ul',
32441                             cls : 'roo-navigation-bar'
32442                         }
32443                     ]
32444                 },
32445                 
32446                 {
32447                     tag : 'div',
32448                     cls : 'roo-navigation-bottom-bar'
32449                 }
32450             ]
32451             
32452         };
32453         
32454         return cfg;
32455         
32456     },
32457     
32458     initEvents: function() 
32459     {
32460         
32461     },
32462     
32463     onRender : function(ct, position) 
32464     {
32465         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32466         
32467         if(this.bullets.length){
32468             Roo.each(this.bullets, function(b){
32469                this.addItem(b);
32470             }, this);
32471         }
32472         
32473         this.format();
32474         
32475     },
32476     
32477     addItem : function(cfg)
32478     {
32479         var item = new Roo.bootstrap.NavProgressItem(cfg);
32480         
32481         item.parentId = this.id;
32482         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32483         
32484         if(cfg.html){
32485             var top = new Roo.bootstrap.Element({
32486                 tag : 'div',
32487                 cls : 'roo-navigation-bar-text'
32488             });
32489             
32490             var bottom = new Roo.bootstrap.Element({
32491                 tag : 'div',
32492                 cls : 'roo-navigation-bar-text'
32493             });
32494             
32495             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32496             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32497             
32498             var topText = new Roo.bootstrap.Element({
32499                 tag : 'span',
32500                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32501             });
32502             
32503             var bottomText = new Roo.bootstrap.Element({
32504                 tag : 'span',
32505                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32506             });
32507             
32508             topText.onRender(top.el, null);
32509             bottomText.onRender(bottom.el, null);
32510             
32511             item.topEl = top;
32512             item.bottomEl = bottom;
32513         }
32514         
32515         this.barItems.push(item);
32516         
32517         return item;
32518     },
32519     
32520     getActive : function()
32521     {
32522         var active = false;
32523         
32524         Roo.each(this.barItems, function(v){
32525             
32526             if (!v.isActive()) {
32527                 return;
32528             }
32529             
32530             active = v;
32531             return false;
32532             
32533         });
32534         
32535         return active;
32536     },
32537     
32538     setActiveItem : function(item)
32539     {
32540         var prev = false;
32541         
32542         Roo.each(this.barItems, function(v){
32543             if (v.rid == item.rid) {
32544                 return ;
32545             }
32546             
32547             if (v.isActive()) {
32548                 v.setActive(false);
32549                 prev = v;
32550             }
32551         });
32552
32553         item.setActive(true);
32554         
32555         this.fireEvent('changed', this, item, prev);
32556     },
32557     
32558     getBarItem: function(rid)
32559     {
32560         var ret = false;
32561         
32562         Roo.each(this.barItems, function(e) {
32563             if (e.rid != rid) {
32564                 return;
32565             }
32566             
32567             ret =  e;
32568             return false;
32569         });
32570         
32571         return ret;
32572     },
32573     
32574     indexOfItem : function(item)
32575     {
32576         var index = false;
32577         
32578         Roo.each(this.barItems, function(v, i){
32579             
32580             if (v.rid != item.rid) {
32581                 return;
32582             }
32583             
32584             index = i;
32585             return false
32586         });
32587         
32588         return index;
32589     },
32590     
32591     setActiveNext : function()
32592     {
32593         var i = this.indexOfItem(this.getActive());
32594         
32595         if (i > this.barItems.length) {
32596             return;
32597         }
32598         
32599         this.setActiveItem(this.barItems[i+1]);
32600     },
32601     
32602     setActivePrev : function()
32603     {
32604         var i = this.indexOfItem(this.getActive());
32605         
32606         if (i  < 1) {
32607             return;
32608         }
32609         
32610         this.setActiveItem(this.barItems[i-1]);
32611     },
32612     
32613     format : function()
32614     {
32615         if(!this.barItems.length){
32616             return;
32617         }
32618      
32619         var width = 100 / this.barItems.length;
32620         
32621         Roo.each(this.barItems, function(i){
32622             i.el.setStyle('width', width + '%');
32623             i.topEl.el.setStyle('width', width + '%');
32624             i.bottomEl.el.setStyle('width', width + '%');
32625         }, this);
32626         
32627     }
32628     
32629 });
32630 /*
32631  * - LGPL
32632  *
32633  * Nav Progress Item
32634  * 
32635  */
32636
32637 /**
32638  * @class Roo.bootstrap.NavProgressItem
32639  * @extends Roo.bootstrap.Component
32640  * Bootstrap NavProgressItem class
32641  * @cfg {String} rid the reference id
32642  * @cfg {Boolean} active (true|false) Is item active default false
32643  * @cfg {Boolean} disabled (true|false) Is item active default false
32644  * @cfg {String} html
32645  * @cfg {String} position (top|bottom) text position default bottom
32646  * @cfg {String} icon show icon instead of number
32647  * 
32648  * @constructor
32649  * Create a new NavProgressItem
32650  * @param {Object} config The config object
32651  */
32652 Roo.bootstrap.NavProgressItem = function(config){
32653     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32654     this.addEvents({
32655         // raw events
32656         /**
32657          * @event click
32658          * The raw click event for the entire grid.
32659          * @param {Roo.bootstrap.NavProgressItem} this
32660          * @param {Roo.EventObject} e
32661          */
32662         "click" : true
32663     });
32664    
32665 };
32666
32667 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32668     
32669     rid : '',
32670     active : false,
32671     disabled : false,
32672     html : '',
32673     position : 'bottom',
32674     icon : false,
32675     
32676     getAutoCreate : function()
32677     {
32678         var iconCls = 'roo-navigation-bar-item-icon';
32679         
32680         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32681         
32682         var cfg = {
32683             tag: 'li',
32684             cls: 'roo-navigation-bar-item',
32685             cn : [
32686                 {
32687                     tag : 'i',
32688                     cls : iconCls
32689                 }
32690             ]
32691         };
32692         
32693         if(this.active){
32694             cfg.cls += ' active';
32695         }
32696         if(this.disabled){
32697             cfg.cls += ' disabled';
32698         }
32699         
32700         return cfg;
32701     },
32702     
32703     disable : function()
32704     {
32705         this.setDisabled(true);
32706     },
32707     
32708     enable : function()
32709     {
32710         this.setDisabled(false);
32711     },
32712     
32713     initEvents: function() 
32714     {
32715         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32716         
32717         this.iconEl.on('click', this.onClick, this);
32718     },
32719     
32720     onClick : function(e)
32721     {
32722         e.preventDefault();
32723         
32724         if(this.disabled){
32725             return;
32726         }
32727         
32728         if(this.fireEvent('click', this, e) === false){
32729             return;
32730         };
32731         
32732         this.parent().setActiveItem(this);
32733     },
32734     
32735     isActive: function () 
32736     {
32737         return this.active;
32738     },
32739     
32740     setActive : function(state)
32741     {
32742         if(this.active == state){
32743             return;
32744         }
32745         
32746         this.active = state;
32747         
32748         if (state) {
32749             this.el.addClass('active');
32750             return;
32751         }
32752         
32753         this.el.removeClass('active');
32754         
32755         return;
32756     },
32757     
32758     setDisabled : function(state)
32759     {
32760         if(this.disabled == state){
32761             return;
32762         }
32763         
32764         this.disabled = state;
32765         
32766         if (state) {
32767             this.el.addClass('disabled');
32768             return;
32769         }
32770         
32771         this.el.removeClass('disabled');
32772     },
32773     
32774     tooltipEl : function()
32775     {
32776         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32777     }
32778 });
32779  
32780
32781  /*
32782  * - LGPL
32783  *
32784  * FieldLabel
32785  * 
32786  */
32787
32788 /**
32789  * @class Roo.bootstrap.FieldLabel
32790  * @extends Roo.bootstrap.Component
32791  * Bootstrap FieldLabel class
32792  * @cfg {String} html contents of the element
32793  * @cfg {String} tag tag of the element default label
32794  * @cfg {String} cls class of the element
32795  * @cfg {String} target label target 
32796  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32797  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32798  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32799  * @cfg {String} iconTooltip default "This field is required"
32800  * @cfg {String} indicatorpos (left|right) default left
32801  * 
32802  * @constructor
32803  * Create a new FieldLabel
32804  * @param {Object} config The config object
32805  */
32806
32807 Roo.bootstrap.FieldLabel = function(config){
32808     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32809     
32810     this.addEvents({
32811             /**
32812              * @event invalid
32813              * Fires after the field has been marked as invalid.
32814              * @param {Roo.form.FieldLabel} this
32815              * @param {String} msg The validation message
32816              */
32817             invalid : true,
32818             /**
32819              * @event valid
32820              * Fires after the field has been validated with no errors.
32821              * @param {Roo.form.FieldLabel} this
32822              */
32823             valid : true
32824         });
32825 };
32826
32827 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32828     
32829     tag: 'label',
32830     cls: '',
32831     html: '',
32832     target: '',
32833     allowBlank : true,
32834     invalidClass : 'has-warning',
32835     validClass : 'has-success',
32836     iconTooltip : 'This field is required',
32837     indicatorpos : 'left',
32838     
32839     getAutoCreate : function(){
32840         
32841         var cls = "";
32842         if (!this.allowBlank) {
32843             cls  = "visible";
32844         }
32845         
32846         var cfg = {
32847             tag : this.tag,
32848             cls : 'roo-bootstrap-field-label ' + this.cls,
32849             for : this.target,
32850             cn : [
32851                 {
32852                     tag : 'i',
32853                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32854                     tooltip : this.iconTooltip
32855                 },
32856                 {
32857                     tag : 'span',
32858                     html : this.html
32859                 }
32860             ] 
32861         };
32862         
32863         if(this.indicatorpos == 'right'){
32864             var cfg = {
32865                 tag : this.tag,
32866                 cls : 'roo-bootstrap-field-label ' + this.cls,
32867                 for : this.target,
32868                 cn : [
32869                     {
32870                         tag : 'span',
32871                         html : this.html
32872                     },
32873                     {
32874                         tag : 'i',
32875                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32876                         tooltip : this.iconTooltip
32877                     }
32878                 ] 
32879             };
32880         }
32881         
32882         return cfg;
32883     },
32884     
32885     initEvents: function() 
32886     {
32887         Roo.bootstrap.Element.superclass.initEvents.call(this);
32888         
32889         this.indicator = this.indicatorEl();
32890         
32891         if(this.indicator){
32892             this.indicator.removeClass('visible');
32893             this.indicator.addClass('invisible');
32894         }
32895         
32896         Roo.bootstrap.FieldLabel.register(this);
32897     },
32898     
32899     indicatorEl : function()
32900     {
32901         var indicator = this.el.select('i.roo-required-indicator',true).first();
32902         
32903         if(!indicator){
32904             return false;
32905         }
32906         
32907         return indicator;
32908         
32909     },
32910     
32911     /**
32912      * Mark this field as valid
32913      */
32914     markValid : function()
32915     {
32916         if(this.indicator){
32917             this.indicator.removeClass('visible');
32918             this.indicator.addClass('invisible');
32919         }
32920         if (Roo.bootstrap.version == 3) {
32921             this.el.removeClass(this.invalidClass);
32922             this.el.addClass(this.validClass);
32923         } else {
32924             this.el.removeClass('is-invalid');
32925             this.el.addClass('is-valid');
32926         }
32927         
32928         
32929         this.fireEvent('valid', this);
32930     },
32931     
32932     /**
32933      * Mark this field as invalid
32934      * @param {String} msg The validation message
32935      */
32936     markInvalid : function(msg)
32937     {
32938         if(this.indicator){
32939             this.indicator.removeClass('invisible');
32940             this.indicator.addClass('visible');
32941         }
32942           if (Roo.bootstrap.version == 3) {
32943             this.el.removeClass(this.validClass);
32944             this.el.addClass(this.invalidClass);
32945         } else {
32946             this.el.removeClass('is-valid');
32947             this.el.addClass('is-invalid');
32948         }
32949         
32950         
32951         this.fireEvent('invalid', this, msg);
32952     }
32953     
32954    
32955 });
32956
32957 Roo.apply(Roo.bootstrap.FieldLabel, {
32958     
32959     groups: {},
32960     
32961      /**
32962     * register a FieldLabel Group
32963     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32964     */
32965     register : function(label)
32966     {
32967         if(this.groups.hasOwnProperty(label.target)){
32968             return;
32969         }
32970      
32971         this.groups[label.target] = label;
32972         
32973     },
32974     /**
32975     * fetch a FieldLabel Group based on the target
32976     * @param {string} target
32977     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32978     */
32979     get: function(target) {
32980         if (typeof(this.groups[target]) == 'undefined') {
32981             return false;
32982         }
32983         
32984         return this.groups[target] ;
32985     }
32986 });
32987
32988  
32989
32990  /*
32991  * - LGPL
32992  *
32993  * page DateSplitField.
32994  * 
32995  */
32996
32997
32998 /**
32999  * @class Roo.bootstrap.DateSplitField
33000  * @extends Roo.bootstrap.Component
33001  * Bootstrap DateSplitField class
33002  * @cfg {string} fieldLabel - the label associated
33003  * @cfg {Number} labelWidth set the width of label (0-12)
33004  * @cfg {String} labelAlign (top|left)
33005  * @cfg {Boolean} dayAllowBlank (true|false) default false
33006  * @cfg {Boolean} monthAllowBlank (true|false) default false
33007  * @cfg {Boolean} yearAllowBlank (true|false) default false
33008  * @cfg {string} dayPlaceholder 
33009  * @cfg {string} monthPlaceholder
33010  * @cfg {string} yearPlaceholder
33011  * @cfg {string} dayFormat default 'd'
33012  * @cfg {string} monthFormat default 'm'
33013  * @cfg {string} yearFormat default 'Y'
33014  * @cfg {Number} labellg set the width of label (1-12)
33015  * @cfg {Number} labelmd set the width of label (1-12)
33016  * @cfg {Number} labelsm set the width of label (1-12)
33017  * @cfg {Number} labelxs set the width of label (1-12)
33018
33019  *     
33020  * @constructor
33021  * Create a new DateSplitField
33022  * @param {Object} config The config object
33023  */
33024
33025 Roo.bootstrap.DateSplitField = function(config){
33026     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33027     
33028     this.addEvents({
33029         // raw events
33030          /**
33031          * @event years
33032          * getting the data of years
33033          * @param {Roo.bootstrap.DateSplitField} this
33034          * @param {Object} years
33035          */
33036         "years" : true,
33037         /**
33038          * @event days
33039          * getting the data of days
33040          * @param {Roo.bootstrap.DateSplitField} this
33041          * @param {Object} days
33042          */
33043         "days" : true,
33044         /**
33045          * @event invalid
33046          * Fires after the field has been marked as invalid.
33047          * @param {Roo.form.Field} this
33048          * @param {String} msg The validation message
33049          */
33050         invalid : true,
33051        /**
33052          * @event valid
33053          * Fires after the field has been validated with no errors.
33054          * @param {Roo.form.Field} this
33055          */
33056         valid : true
33057     });
33058 };
33059
33060 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33061     
33062     fieldLabel : '',
33063     labelAlign : 'top',
33064     labelWidth : 3,
33065     dayAllowBlank : false,
33066     monthAllowBlank : false,
33067     yearAllowBlank : false,
33068     dayPlaceholder : '',
33069     monthPlaceholder : '',
33070     yearPlaceholder : '',
33071     dayFormat : 'd',
33072     monthFormat : 'm',
33073     yearFormat : 'Y',
33074     isFormField : true,
33075     labellg : 0,
33076     labelmd : 0,
33077     labelsm : 0,
33078     labelxs : 0,
33079     
33080     getAutoCreate : function()
33081     {
33082         var cfg = {
33083             tag : 'div',
33084             cls : 'row roo-date-split-field-group',
33085             cn : [
33086                 {
33087                     tag : 'input',
33088                     type : 'hidden',
33089                     cls : 'form-hidden-field roo-date-split-field-group-value',
33090                     name : this.name
33091                 }
33092             ]
33093         };
33094         
33095         var labelCls = 'col-md-12';
33096         var contentCls = 'col-md-4';
33097         
33098         if(this.fieldLabel){
33099             
33100             var label = {
33101                 tag : 'div',
33102                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33103                 cn : [
33104                     {
33105                         tag : 'label',
33106                         html : this.fieldLabel
33107                     }
33108                 ]
33109             };
33110             
33111             if(this.labelAlign == 'left'){
33112             
33113                 if(this.labelWidth > 12){
33114                     label.style = "width: " + this.labelWidth + 'px';
33115                 }
33116
33117                 if(this.labelWidth < 13 && this.labelmd == 0){
33118                     this.labelmd = this.labelWidth;
33119                 }
33120
33121                 if(this.labellg > 0){
33122                     labelCls = ' col-lg-' + this.labellg;
33123                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33124                 }
33125
33126                 if(this.labelmd > 0){
33127                     labelCls = ' col-md-' + this.labelmd;
33128                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33129                 }
33130
33131                 if(this.labelsm > 0){
33132                     labelCls = ' col-sm-' + this.labelsm;
33133                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33134                 }
33135
33136                 if(this.labelxs > 0){
33137                     labelCls = ' col-xs-' + this.labelxs;
33138                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33139                 }
33140             }
33141             
33142             label.cls += ' ' + labelCls;
33143             
33144             cfg.cn.push(label);
33145         }
33146         
33147         Roo.each(['day', 'month', 'year'], function(t){
33148             cfg.cn.push({
33149                 tag : 'div',
33150                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33151             });
33152         }, this);
33153         
33154         return cfg;
33155     },
33156     
33157     inputEl: function ()
33158     {
33159         return this.el.select('.roo-date-split-field-group-value', true).first();
33160     },
33161     
33162     onRender : function(ct, position) 
33163     {
33164         var _this = this;
33165         
33166         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33167         
33168         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33169         
33170         this.dayField = new Roo.bootstrap.ComboBox({
33171             allowBlank : this.dayAllowBlank,
33172             alwaysQuery : true,
33173             displayField : 'value',
33174             editable : false,
33175             fieldLabel : '',
33176             forceSelection : true,
33177             mode : 'local',
33178             placeholder : this.dayPlaceholder,
33179             selectOnFocus : true,
33180             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33181             triggerAction : 'all',
33182             typeAhead : true,
33183             valueField : 'value',
33184             store : new Roo.data.SimpleStore({
33185                 data : (function() {    
33186                     var days = [];
33187                     _this.fireEvent('days', _this, days);
33188                     return days;
33189                 })(),
33190                 fields : [ 'value' ]
33191             }),
33192             listeners : {
33193                 select : function (_self, record, index)
33194                 {
33195                     _this.setValue(_this.getValue());
33196                 }
33197             }
33198         });
33199
33200         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33201         
33202         this.monthField = new Roo.bootstrap.MonthField({
33203             after : '<i class=\"fa fa-calendar\"></i>',
33204             allowBlank : this.monthAllowBlank,
33205             placeholder : this.monthPlaceholder,
33206             readOnly : true,
33207             listeners : {
33208                 render : function (_self)
33209                 {
33210                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33211                         e.preventDefault();
33212                         _self.focus();
33213                     });
33214                 },
33215                 select : function (_self, oldvalue, newvalue)
33216                 {
33217                     _this.setValue(_this.getValue());
33218                 }
33219             }
33220         });
33221         
33222         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33223         
33224         this.yearField = new Roo.bootstrap.ComboBox({
33225             allowBlank : this.yearAllowBlank,
33226             alwaysQuery : true,
33227             displayField : 'value',
33228             editable : false,
33229             fieldLabel : '',
33230             forceSelection : true,
33231             mode : 'local',
33232             placeholder : this.yearPlaceholder,
33233             selectOnFocus : true,
33234             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33235             triggerAction : 'all',
33236             typeAhead : true,
33237             valueField : 'value',
33238             store : new Roo.data.SimpleStore({
33239                 data : (function() {
33240                     var years = [];
33241                     _this.fireEvent('years', _this, years);
33242                     return years;
33243                 })(),
33244                 fields : [ 'value' ]
33245             }),
33246             listeners : {
33247                 select : function (_self, record, index)
33248                 {
33249                     _this.setValue(_this.getValue());
33250                 }
33251             }
33252         });
33253
33254         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33255     },
33256     
33257     setValue : function(v, format)
33258     {
33259         this.inputEl.dom.value = v;
33260         
33261         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33262         
33263         var d = Date.parseDate(v, f);
33264         
33265         if(!d){
33266             this.validate();
33267             return;
33268         }
33269         
33270         this.setDay(d.format(this.dayFormat));
33271         this.setMonth(d.format(this.monthFormat));
33272         this.setYear(d.format(this.yearFormat));
33273         
33274         this.validate();
33275         
33276         return;
33277     },
33278     
33279     setDay : function(v)
33280     {
33281         this.dayField.setValue(v);
33282         this.inputEl.dom.value = this.getValue();
33283         this.validate();
33284         return;
33285     },
33286     
33287     setMonth : function(v)
33288     {
33289         this.monthField.setValue(v, true);
33290         this.inputEl.dom.value = this.getValue();
33291         this.validate();
33292         return;
33293     },
33294     
33295     setYear : function(v)
33296     {
33297         this.yearField.setValue(v);
33298         this.inputEl.dom.value = this.getValue();
33299         this.validate();
33300         return;
33301     },
33302     
33303     getDay : function()
33304     {
33305         return this.dayField.getValue();
33306     },
33307     
33308     getMonth : function()
33309     {
33310         return this.monthField.getValue();
33311     },
33312     
33313     getYear : function()
33314     {
33315         return this.yearField.getValue();
33316     },
33317     
33318     getValue : function()
33319     {
33320         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33321         
33322         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33323         
33324         return date;
33325     },
33326     
33327     reset : function()
33328     {
33329         this.setDay('');
33330         this.setMonth('');
33331         this.setYear('');
33332         this.inputEl.dom.value = '';
33333         this.validate();
33334         return;
33335     },
33336     
33337     validate : function()
33338     {
33339         var d = this.dayField.validate();
33340         var m = this.monthField.validate();
33341         var y = this.yearField.validate();
33342         
33343         var valid = true;
33344         
33345         if(
33346                 (!this.dayAllowBlank && !d) ||
33347                 (!this.monthAllowBlank && !m) ||
33348                 (!this.yearAllowBlank && !y)
33349         ){
33350             valid = false;
33351         }
33352         
33353         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33354             return valid;
33355         }
33356         
33357         if(valid){
33358             this.markValid();
33359             return valid;
33360         }
33361         
33362         this.markInvalid();
33363         
33364         return valid;
33365     },
33366     
33367     markValid : function()
33368     {
33369         
33370         var label = this.el.select('label', true).first();
33371         var icon = this.el.select('i.fa-star', true).first();
33372
33373         if(label && icon){
33374             icon.remove();
33375         }
33376         
33377         this.fireEvent('valid', this);
33378     },
33379     
33380      /**
33381      * Mark this field as invalid
33382      * @param {String} msg The validation message
33383      */
33384     markInvalid : function(msg)
33385     {
33386         
33387         var label = this.el.select('label', true).first();
33388         var icon = this.el.select('i.fa-star', true).first();
33389
33390         if(label && !icon){
33391             this.el.select('.roo-date-split-field-label', true).createChild({
33392                 tag : 'i',
33393                 cls : 'text-danger fa fa-lg fa-star',
33394                 tooltip : 'This field is required',
33395                 style : 'margin-right:5px;'
33396             }, label, true);
33397         }
33398         
33399         this.fireEvent('invalid', this, msg);
33400     },
33401     
33402     clearInvalid : function()
33403     {
33404         var label = this.el.select('label', true).first();
33405         var icon = this.el.select('i.fa-star', true).first();
33406
33407         if(label && icon){
33408             icon.remove();
33409         }
33410         
33411         this.fireEvent('valid', this);
33412     },
33413     
33414     getName: function()
33415     {
33416         return this.name;
33417     }
33418     
33419 });
33420
33421  /**
33422  *
33423  * This is based on 
33424  * http://masonry.desandro.com
33425  *
33426  * The idea is to render all the bricks based on vertical width...
33427  *
33428  * The original code extends 'outlayer' - we might need to use that....
33429  * 
33430  */
33431
33432
33433 /**
33434  * @class Roo.bootstrap.LayoutMasonry
33435  * @extends Roo.bootstrap.Component
33436  * Bootstrap Layout Masonry class
33437  * 
33438  * @constructor
33439  * Create a new Element
33440  * @param {Object} config The config object
33441  */
33442
33443 Roo.bootstrap.LayoutMasonry = function(config){
33444     
33445     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33446     
33447     this.bricks = [];
33448     
33449     Roo.bootstrap.LayoutMasonry.register(this);
33450     
33451     this.addEvents({
33452         // raw events
33453         /**
33454          * @event layout
33455          * Fire after layout the items
33456          * @param {Roo.bootstrap.LayoutMasonry} this
33457          * @param {Roo.EventObject} e
33458          */
33459         "layout" : true
33460     });
33461     
33462 };
33463
33464 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33465     
33466     /**
33467      * @cfg {Boolean} isLayoutInstant = no animation?
33468      */   
33469     isLayoutInstant : false, // needed?
33470    
33471     /**
33472      * @cfg {Number} boxWidth  width of the columns
33473      */   
33474     boxWidth : 450,
33475     
33476       /**
33477      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33478      */   
33479     boxHeight : 0,
33480     
33481     /**
33482      * @cfg {Number} padWidth padding below box..
33483      */   
33484     padWidth : 10, 
33485     
33486     /**
33487      * @cfg {Number} gutter gutter width..
33488      */   
33489     gutter : 10,
33490     
33491      /**
33492      * @cfg {Number} maxCols maximum number of columns
33493      */   
33494     
33495     maxCols: 0,
33496     
33497     /**
33498      * @cfg {Boolean} isAutoInitial defalut true
33499      */   
33500     isAutoInitial : true, 
33501     
33502     containerWidth: 0,
33503     
33504     /**
33505      * @cfg {Boolean} isHorizontal defalut false
33506      */   
33507     isHorizontal : false, 
33508
33509     currentSize : null,
33510     
33511     tag: 'div',
33512     
33513     cls: '',
33514     
33515     bricks: null, //CompositeElement
33516     
33517     cols : 1,
33518     
33519     _isLayoutInited : false,
33520     
33521 //    isAlternative : false, // only use for vertical layout...
33522     
33523     /**
33524      * @cfg {Number} alternativePadWidth padding below box..
33525      */   
33526     alternativePadWidth : 50,
33527     
33528     selectedBrick : [],
33529     
33530     getAutoCreate : function(){
33531         
33532         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33533         
33534         var cfg = {
33535             tag: this.tag,
33536             cls: 'blog-masonary-wrapper ' + this.cls,
33537             cn : {
33538                 cls : 'mas-boxes masonary'
33539             }
33540         };
33541         
33542         return cfg;
33543     },
33544     
33545     getChildContainer: function( )
33546     {
33547         if (this.boxesEl) {
33548             return this.boxesEl;
33549         }
33550         
33551         this.boxesEl = this.el.select('.mas-boxes').first();
33552         
33553         return this.boxesEl;
33554     },
33555     
33556     
33557     initEvents : function()
33558     {
33559         var _this = this;
33560         
33561         if(this.isAutoInitial){
33562             Roo.log('hook children rendered');
33563             this.on('childrenrendered', function() {
33564                 Roo.log('children rendered');
33565                 _this.initial();
33566             } ,this);
33567         }
33568     },
33569     
33570     initial : function()
33571     {
33572         this.selectedBrick = [];
33573         
33574         this.currentSize = this.el.getBox(true);
33575         
33576         Roo.EventManager.onWindowResize(this.resize, this); 
33577
33578         if(!this.isAutoInitial){
33579             this.layout();
33580             return;
33581         }
33582         
33583         this.layout();
33584         
33585         return;
33586         //this.layout.defer(500,this);
33587         
33588     },
33589     
33590     resize : function()
33591     {
33592         var cs = this.el.getBox(true);
33593         
33594         if (
33595                 this.currentSize.width == cs.width && 
33596                 this.currentSize.x == cs.x && 
33597                 this.currentSize.height == cs.height && 
33598                 this.currentSize.y == cs.y 
33599         ) {
33600             Roo.log("no change in with or X or Y");
33601             return;
33602         }
33603         
33604         this.currentSize = cs;
33605         
33606         this.layout();
33607         
33608     },
33609     
33610     layout : function()
33611     {   
33612         this._resetLayout();
33613         
33614         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33615         
33616         this.layoutItems( isInstant );
33617       
33618         this._isLayoutInited = true;
33619         
33620         this.fireEvent('layout', this);
33621         
33622     },
33623     
33624     _resetLayout : function()
33625     {
33626         if(this.isHorizontal){
33627             this.horizontalMeasureColumns();
33628             return;
33629         }
33630         
33631         this.verticalMeasureColumns();
33632         
33633     },
33634     
33635     verticalMeasureColumns : function()
33636     {
33637         this.getContainerWidth();
33638         
33639 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33640 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33641 //            return;
33642 //        }
33643         
33644         var boxWidth = this.boxWidth + this.padWidth;
33645         
33646         if(this.containerWidth < this.boxWidth){
33647             boxWidth = this.containerWidth
33648         }
33649         
33650         var containerWidth = this.containerWidth;
33651         
33652         var cols = Math.floor(containerWidth / boxWidth);
33653         
33654         this.cols = Math.max( cols, 1 );
33655         
33656         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33657         
33658         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33659         
33660         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33661         
33662         this.colWidth = boxWidth + avail - this.padWidth;
33663         
33664         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33665         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33666     },
33667     
33668     horizontalMeasureColumns : function()
33669     {
33670         this.getContainerWidth();
33671         
33672         var boxWidth = this.boxWidth;
33673         
33674         if(this.containerWidth < boxWidth){
33675             boxWidth = this.containerWidth;
33676         }
33677         
33678         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33679         
33680         this.el.setHeight(boxWidth);
33681         
33682     },
33683     
33684     getContainerWidth : function()
33685     {
33686         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33687     },
33688     
33689     layoutItems : function( isInstant )
33690     {
33691         Roo.log(this.bricks);
33692         
33693         var items = Roo.apply([], this.bricks);
33694         
33695         if(this.isHorizontal){
33696             this._horizontalLayoutItems( items , isInstant );
33697             return;
33698         }
33699         
33700 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33701 //            this._verticalAlternativeLayoutItems( items , isInstant );
33702 //            return;
33703 //        }
33704         
33705         this._verticalLayoutItems( items , isInstant );
33706         
33707     },
33708     
33709     _verticalLayoutItems : function ( items , isInstant)
33710     {
33711         if ( !items || !items.length ) {
33712             return;
33713         }
33714         
33715         var standard = [
33716             ['xs', 'xs', 'xs', 'tall'],
33717             ['xs', 'xs', 'tall'],
33718             ['xs', 'xs', 'sm'],
33719             ['xs', 'xs', 'xs'],
33720             ['xs', 'tall'],
33721             ['xs', 'sm'],
33722             ['xs', 'xs'],
33723             ['xs'],
33724             
33725             ['sm', 'xs', 'xs'],
33726             ['sm', 'xs'],
33727             ['sm'],
33728             
33729             ['tall', 'xs', 'xs', 'xs'],
33730             ['tall', 'xs', 'xs'],
33731             ['tall', 'xs'],
33732             ['tall']
33733             
33734         ];
33735         
33736         var queue = [];
33737         
33738         var boxes = [];
33739         
33740         var box = [];
33741         
33742         Roo.each(items, function(item, k){
33743             
33744             switch (item.size) {
33745                 // these layouts take up a full box,
33746                 case 'md' :
33747                 case 'md-left' :
33748                 case 'md-right' :
33749                 case 'wide' :
33750                     
33751                     if(box.length){
33752                         boxes.push(box);
33753                         box = [];
33754                     }
33755                     
33756                     boxes.push([item]);
33757                     
33758                     break;
33759                     
33760                 case 'xs' :
33761                 case 'sm' :
33762                 case 'tall' :
33763                     
33764                     box.push(item);
33765                     
33766                     break;
33767                 default :
33768                     break;
33769                     
33770             }
33771             
33772         }, this);
33773         
33774         if(box.length){
33775             boxes.push(box);
33776             box = [];
33777         }
33778         
33779         var filterPattern = function(box, length)
33780         {
33781             if(!box.length){
33782                 return;
33783             }
33784             
33785             var match = false;
33786             
33787             var pattern = box.slice(0, length);
33788             
33789             var format = [];
33790             
33791             Roo.each(pattern, function(i){
33792                 format.push(i.size);
33793             }, this);
33794             
33795             Roo.each(standard, function(s){
33796                 
33797                 if(String(s) != String(format)){
33798                     return;
33799                 }
33800                 
33801                 match = true;
33802                 return false;
33803                 
33804             }, this);
33805             
33806             if(!match && length == 1){
33807                 return;
33808             }
33809             
33810             if(!match){
33811                 filterPattern(box, length - 1);
33812                 return;
33813             }
33814                 
33815             queue.push(pattern);
33816
33817             box = box.slice(length, box.length);
33818
33819             filterPattern(box, 4);
33820
33821             return;
33822             
33823         }
33824         
33825         Roo.each(boxes, function(box, k){
33826             
33827             if(!box.length){
33828                 return;
33829             }
33830             
33831             if(box.length == 1){
33832                 queue.push(box);
33833                 return;
33834             }
33835             
33836             filterPattern(box, 4);
33837             
33838         }, this);
33839         
33840         this._processVerticalLayoutQueue( queue, isInstant );
33841         
33842     },
33843     
33844 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33845 //    {
33846 //        if ( !items || !items.length ) {
33847 //            return;
33848 //        }
33849 //
33850 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33851 //        
33852 //    },
33853     
33854     _horizontalLayoutItems : function ( items , isInstant)
33855     {
33856         if ( !items || !items.length || items.length < 3) {
33857             return;
33858         }
33859         
33860         items.reverse();
33861         
33862         var eItems = items.slice(0, 3);
33863         
33864         items = items.slice(3, items.length);
33865         
33866         var standard = [
33867             ['xs', 'xs', 'xs', 'wide'],
33868             ['xs', 'xs', 'wide'],
33869             ['xs', 'xs', 'sm'],
33870             ['xs', 'xs', 'xs'],
33871             ['xs', 'wide'],
33872             ['xs', 'sm'],
33873             ['xs', 'xs'],
33874             ['xs'],
33875             
33876             ['sm', 'xs', 'xs'],
33877             ['sm', 'xs'],
33878             ['sm'],
33879             
33880             ['wide', 'xs', 'xs', 'xs'],
33881             ['wide', 'xs', 'xs'],
33882             ['wide', 'xs'],
33883             ['wide'],
33884             
33885             ['wide-thin']
33886         ];
33887         
33888         var queue = [];
33889         
33890         var boxes = [];
33891         
33892         var box = [];
33893         
33894         Roo.each(items, function(item, k){
33895             
33896             switch (item.size) {
33897                 case 'md' :
33898                 case 'md-left' :
33899                 case 'md-right' :
33900                 case 'tall' :
33901                     
33902                     if(box.length){
33903                         boxes.push(box);
33904                         box = [];
33905                     }
33906                     
33907                     boxes.push([item]);
33908                     
33909                     break;
33910                     
33911                 case 'xs' :
33912                 case 'sm' :
33913                 case 'wide' :
33914                 case 'wide-thin' :
33915                     
33916                     box.push(item);
33917                     
33918                     break;
33919                 default :
33920                     break;
33921                     
33922             }
33923             
33924         }, this);
33925         
33926         if(box.length){
33927             boxes.push(box);
33928             box = [];
33929         }
33930         
33931         var filterPattern = function(box, length)
33932         {
33933             if(!box.length){
33934                 return;
33935             }
33936             
33937             var match = false;
33938             
33939             var pattern = box.slice(0, length);
33940             
33941             var format = [];
33942             
33943             Roo.each(pattern, function(i){
33944                 format.push(i.size);
33945             }, this);
33946             
33947             Roo.each(standard, function(s){
33948                 
33949                 if(String(s) != String(format)){
33950                     return;
33951                 }
33952                 
33953                 match = true;
33954                 return false;
33955                 
33956             }, this);
33957             
33958             if(!match && length == 1){
33959                 return;
33960             }
33961             
33962             if(!match){
33963                 filterPattern(box, length - 1);
33964                 return;
33965             }
33966                 
33967             queue.push(pattern);
33968
33969             box = box.slice(length, box.length);
33970
33971             filterPattern(box, 4);
33972
33973             return;
33974             
33975         }
33976         
33977         Roo.each(boxes, function(box, k){
33978             
33979             if(!box.length){
33980                 return;
33981             }
33982             
33983             if(box.length == 1){
33984                 queue.push(box);
33985                 return;
33986             }
33987             
33988             filterPattern(box, 4);
33989             
33990         }, this);
33991         
33992         
33993         var prune = [];
33994         
33995         var pos = this.el.getBox(true);
33996         
33997         var minX = pos.x;
33998         
33999         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34000         
34001         var hit_end = false;
34002         
34003         Roo.each(queue, function(box){
34004             
34005             if(hit_end){
34006                 
34007                 Roo.each(box, function(b){
34008                 
34009                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34010                     b.el.hide();
34011
34012                 }, this);
34013
34014                 return;
34015             }
34016             
34017             var mx = 0;
34018             
34019             Roo.each(box, function(b){
34020                 
34021                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34022                 b.el.show();
34023
34024                 mx = Math.max(mx, b.x);
34025                 
34026             }, this);
34027             
34028             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34029             
34030             if(maxX < minX){
34031                 
34032                 Roo.each(box, function(b){
34033                 
34034                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34035                     b.el.hide();
34036                     
34037                 }, this);
34038                 
34039                 hit_end = true;
34040                 
34041                 return;
34042             }
34043             
34044             prune.push(box);
34045             
34046         }, this);
34047         
34048         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34049     },
34050     
34051     /** Sets position of item in DOM
34052     * @param {Element} item
34053     * @param {Number} x - horizontal position
34054     * @param {Number} y - vertical position
34055     * @param {Boolean} isInstant - disables transitions
34056     */
34057     _processVerticalLayoutQueue : function( queue, isInstant )
34058     {
34059         var pos = this.el.getBox(true);
34060         var x = pos.x;
34061         var y = pos.y;
34062         var maxY = [];
34063         
34064         for (var i = 0; i < this.cols; i++){
34065             maxY[i] = pos.y;
34066         }
34067         
34068         Roo.each(queue, function(box, k){
34069             
34070             var col = k % this.cols;
34071             
34072             Roo.each(box, function(b,kk){
34073                 
34074                 b.el.position('absolute');
34075                 
34076                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34077                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34078                 
34079                 if(b.size == 'md-left' || b.size == 'md-right'){
34080                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34081                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34082                 }
34083                 
34084                 b.el.setWidth(width);
34085                 b.el.setHeight(height);
34086                 // iframe?
34087                 b.el.select('iframe',true).setSize(width,height);
34088                 
34089             }, this);
34090             
34091             for (var i = 0; i < this.cols; i++){
34092                 
34093                 if(maxY[i] < maxY[col]){
34094                     col = i;
34095                     continue;
34096                 }
34097                 
34098                 col = Math.min(col, i);
34099                 
34100             }
34101             
34102             x = pos.x + col * (this.colWidth + this.padWidth);
34103             
34104             y = maxY[col];
34105             
34106             var positions = [];
34107             
34108             switch (box.length){
34109                 case 1 :
34110                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34111                     break;
34112                 case 2 :
34113                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34114                     break;
34115                 case 3 :
34116                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34117                     break;
34118                 case 4 :
34119                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34120                     break;
34121                 default :
34122                     break;
34123             }
34124             
34125             Roo.each(box, function(b,kk){
34126                 
34127                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34128                 
34129                 var sz = b.el.getSize();
34130                 
34131                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34132                 
34133             }, this);
34134             
34135         }, this);
34136         
34137         var mY = 0;
34138         
34139         for (var i = 0; i < this.cols; i++){
34140             mY = Math.max(mY, maxY[i]);
34141         }
34142         
34143         this.el.setHeight(mY - pos.y);
34144         
34145     },
34146     
34147 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34148 //    {
34149 //        var pos = this.el.getBox(true);
34150 //        var x = pos.x;
34151 //        var y = pos.y;
34152 //        var maxX = pos.right;
34153 //        
34154 //        var maxHeight = 0;
34155 //        
34156 //        Roo.each(items, function(item, k){
34157 //            
34158 //            var c = k % 2;
34159 //            
34160 //            item.el.position('absolute');
34161 //                
34162 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34163 //
34164 //            item.el.setWidth(width);
34165 //
34166 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34167 //
34168 //            item.el.setHeight(height);
34169 //            
34170 //            if(c == 0){
34171 //                item.el.setXY([x, y], isInstant ? false : true);
34172 //            } else {
34173 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34174 //            }
34175 //            
34176 //            y = y + height + this.alternativePadWidth;
34177 //            
34178 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34179 //            
34180 //        }, this);
34181 //        
34182 //        this.el.setHeight(maxHeight);
34183 //        
34184 //    },
34185     
34186     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34187     {
34188         var pos = this.el.getBox(true);
34189         
34190         var minX = pos.x;
34191         var minY = pos.y;
34192         
34193         var maxX = pos.right;
34194         
34195         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34196         
34197         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34198         
34199         Roo.each(queue, function(box, k){
34200             
34201             Roo.each(box, function(b, kk){
34202                 
34203                 b.el.position('absolute');
34204                 
34205                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34206                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34207                 
34208                 if(b.size == 'md-left' || b.size == 'md-right'){
34209                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34210                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34211                 }
34212                 
34213                 b.el.setWidth(width);
34214                 b.el.setHeight(height);
34215                 
34216             }, this);
34217             
34218             if(!box.length){
34219                 return;
34220             }
34221             
34222             var positions = [];
34223             
34224             switch (box.length){
34225                 case 1 :
34226                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34227                     break;
34228                 case 2 :
34229                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34230                     break;
34231                 case 3 :
34232                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34233                     break;
34234                 case 4 :
34235                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34236                     break;
34237                 default :
34238                     break;
34239             }
34240             
34241             Roo.each(box, function(b,kk){
34242                 
34243                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34244                 
34245                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34246                 
34247             }, this);
34248             
34249         }, this);
34250         
34251     },
34252     
34253     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34254     {
34255         Roo.each(eItems, function(b,k){
34256             
34257             b.size = (k == 0) ? 'sm' : 'xs';
34258             b.x = (k == 0) ? 2 : 1;
34259             b.y = (k == 0) ? 2 : 1;
34260             
34261             b.el.position('absolute');
34262             
34263             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34264                 
34265             b.el.setWidth(width);
34266             
34267             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34268             
34269             b.el.setHeight(height);
34270             
34271         }, this);
34272
34273         var positions = [];
34274         
34275         positions.push({
34276             x : maxX - this.unitWidth * 2 - this.gutter,
34277             y : minY
34278         });
34279         
34280         positions.push({
34281             x : maxX - this.unitWidth,
34282             y : minY + (this.unitWidth + this.gutter) * 2
34283         });
34284         
34285         positions.push({
34286             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34287             y : minY
34288         });
34289         
34290         Roo.each(eItems, function(b,k){
34291             
34292             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34293
34294         }, this);
34295         
34296     },
34297     
34298     getVerticalOneBoxColPositions : function(x, y, box)
34299     {
34300         var pos = [];
34301         
34302         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34303         
34304         if(box[0].size == 'md-left'){
34305             rand = 0;
34306         }
34307         
34308         if(box[0].size == 'md-right'){
34309             rand = 1;
34310         }
34311         
34312         pos.push({
34313             x : x + (this.unitWidth + this.gutter) * rand,
34314             y : y
34315         });
34316         
34317         return pos;
34318     },
34319     
34320     getVerticalTwoBoxColPositions : function(x, y, box)
34321     {
34322         var pos = [];
34323         
34324         if(box[0].size == 'xs'){
34325             
34326             pos.push({
34327                 x : x,
34328                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34329             });
34330
34331             pos.push({
34332                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34333                 y : y
34334             });
34335             
34336             return pos;
34337             
34338         }
34339         
34340         pos.push({
34341             x : x,
34342             y : y
34343         });
34344
34345         pos.push({
34346             x : x + (this.unitWidth + this.gutter) * 2,
34347             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34348         });
34349         
34350         return pos;
34351         
34352     },
34353     
34354     getVerticalThreeBoxColPositions : function(x, y, box)
34355     {
34356         var pos = [];
34357         
34358         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34359             
34360             pos.push({
34361                 x : x,
34362                 y : y
34363             });
34364
34365             pos.push({
34366                 x : x + (this.unitWidth + this.gutter) * 1,
34367                 y : y
34368             });
34369             
34370             pos.push({
34371                 x : x + (this.unitWidth + this.gutter) * 2,
34372                 y : y
34373             });
34374             
34375             return pos;
34376             
34377         }
34378         
34379         if(box[0].size == 'xs' && box[1].size == 'xs'){
34380             
34381             pos.push({
34382                 x : x,
34383                 y : y
34384             });
34385
34386             pos.push({
34387                 x : x,
34388                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34389             });
34390             
34391             pos.push({
34392                 x : x + (this.unitWidth + this.gutter) * 1,
34393                 y : y
34394             });
34395             
34396             return pos;
34397             
34398         }
34399         
34400         pos.push({
34401             x : x,
34402             y : y
34403         });
34404
34405         pos.push({
34406             x : x + (this.unitWidth + this.gutter) * 2,
34407             y : y
34408         });
34409
34410         pos.push({
34411             x : x + (this.unitWidth + this.gutter) * 2,
34412             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34413         });
34414             
34415         return pos;
34416         
34417     },
34418     
34419     getVerticalFourBoxColPositions : function(x, y, box)
34420     {
34421         var pos = [];
34422         
34423         if(box[0].size == 'xs'){
34424             
34425             pos.push({
34426                 x : x,
34427                 y : y
34428             });
34429
34430             pos.push({
34431                 x : x,
34432                 y : y + (this.unitHeight + this.gutter) * 1
34433             });
34434             
34435             pos.push({
34436                 x : x,
34437                 y : y + (this.unitHeight + this.gutter) * 2
34438             });
34439             
34440             pos.push({
34441                 x : x + (this.unitWidth + this.gutter) * 1,
34442                 y : y
34443             });
34444             
34445             return pos;
34446             
34447         }
34448         
34449         pos.push({
34450             x : x,
34451             y : y
34452         });
34453
34454         pos.push({
34455             x : x + (this.unitWidth + this.gutter) * 2,
34456             y : y
34457         });
34458
34459         pos.push({
34460             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34461             y : y + (this.unitHeight + this.gutter) * 1
34462         });
34463
34464         pos.push({
34465             x : x + (this.unitWidth + this.gutter) * 2,
34466             y : y + (this.unitWidth + this.gutter) * 2
34467         });
34468
34469         return pos;
34470         
34471     },
34472     
34473     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34474     {
34475         var pos = [];
34476         
34477         if(box[0].size == 'md-left'){
34478             pos.push({
34479                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34480                 y : minY
34481             });
34482             
34483             return pos;
34484         }
34485         
34486         if(box[0].size == 'md-right'){
34487             pos.push({
34488                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34489                 y : minY + (this.unitWidth + this.gutter) * 1
34490             });
34491             
34492             return pos;
34493         }
34494         
34495         var rand = Math.floor(Math.random() * (4 - box[0].y));
34496         
34497         pos.push({
34498             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34499             y : minY + (this.unitWidth + this.gutter) * rand
34500         });
34501         
34502         return pos;
34503         
34504     },
34505     
34506     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34507     {
34508         var pos = [];
34509         
34510         if(box[0].size == 'xs'){
34511             
34512             pos.push({
34513                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34514                 y : minY
34515             });
34516
34517             pos.push({
34518                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34519                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34520             });
34521             
34522             return pos;
34523             
34524         }
34525         
34526         pos.push({
34527             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34528             y : minY
34529         });
34530
34531         pos.push({
34532             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34533             y : minY + (this.unitWidth + this.gutter) * 2
34534         });
34535         
34536         return pos;
34537         
34538     },
34539     
34540     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34541     {
34542         var pos = [];
34543         
34544         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34545             
34546             pos.push({
34547                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34548                 y : minY
34549             });
34550
34551             pos.push({
34552                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34553                 y : minY + (this.unitWidth + this.gutter) * 1
34554             });
34555             
34556             pos.push({
34557                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34558                 y : minY + (this.unitWidth + this.gutter) * 2
34559             });
34560             
34561             return pos;
34562             
34563         }
34564         
34565         if(box[0].size == 'xs' && box[1].size == 'xs'){
34566             
34567             pos.push({
34568                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34569                 y : minY
34570             });
34571
34572             pos.push({
34573                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34574                 y : minY
34575             });
34576             
34577             pos.push({
34578                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34579                 y : minY + (this.unitWidth + this.gutter) * 1
34580             });
34581             
34582             return pos;
34583             
34584         }
34585         
34586         pos.push({
34587             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34588             y : minY
34589         });
34590
34591         pos.push({
34592             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34593             y : minY + (this.unitWidth + this.gutter) * 2
34594         });
34595
34596         pos.push({
34597             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34598             y : minY + (this.unitWidth + this.gutter) * 2
34599         });
34600             
34601         return pos;
34602         
34603     },
34604     
34605     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34606     {
34607         var pos = [];
34608         
34609         if(box[0].size == 'xs'){
34610             
34611             pos.push({
34612                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34613                 y : minY
34614             });
34615
34616             pos.push({
34617                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34618                 y : minY
34619             });
34620             
34621             pos.push({
34622                 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),
34623                 y : minY
34624             });
34625             
34626             pos.push({
34627                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34628                 y : minY + (this.unitWidth + this.gutter) * 1
34629             });
34630             
34631             return pos;
34632             
34633         }
34634         
34635         pos.push({
34636             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34637             y : minY
34638         });
34639         
34640         pos.push({
34641             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34642             y : minY + (this.unitWidth + this.gutter) * 2
34643         });
34644         
34645         pos.push({
34646             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34647             y : minY + (this.unitWidth + this.gutter) * 2
34648         });
34649         
34650         pos.push({
34651             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),
34652             y : minY + (this.unitWidth + this.gutter) * 2
34653         });
34654
34655         return pos;
34656         
34657     },
34658     
34659     /**
34660     * remove a Masonry Brick
34661     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34662     */
34663     removeBrick : function(brick_id)
34664     {
34665         if (!brick_id) {
34666             return;
34667         }
34668         
34669         for (var i = 0; i<this.bricks.length; i++) {
34670             if (this.bricks[i].id == brick_id) {
34671                 this.bricks.splice(i,1);
34672                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34673                 this.initial();
34674             }
34675         }
34676     },
34677     
34678     /**
34679     * adds a Masonry Brick
34680     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34681     */
34682     addBrick : function(cfg)
34683     {
34684         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34685         //this.register(cn);
34686         cn.parentId = this.id;
34687         cn.render(this.el);
34688         return cn;
34689     },
34690     
34691     /**
34692     * register a Masonry Brick
34693     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34694     */
34695     
34696     register : function(brick)
34697     {
34698         this.bricks.push(brick);
34699         brick.masonryId = this.id;
34700     },
34701     
34702     /**
34703     * clear all the Masonry Brick
34704     */
34705     clearAll : function()
34706     {
34707         this.bricks = [];
34708         //this.getChildContainer().dom.innerHTML = "";
34709         this.el.dom.innerHTML = '';
34710     },
34711     
34712     getSelected : function()
34713     {
34714         if (!this.selectedBrick) {
34715             return false;
34716         }
34717         
34718         return this.selectedBrick;
34719     }
34720 });
34721
34722 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34723     
34724     groups: {},
34725      /**
34726     * register a Masonry Layout
34727     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34728     */
34729     
34730     register : function(layout)
34731     {
34732         this.groups[layout.id] = layout;
34733     },
34734     /**
34735     * fetch a  Masonry Layout based on the masonry layout ID
34736     * @param {string} the masonry layout to add
34737     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34738     */
34739     
34740     get: function(layout_id) {
34741         if (typeof(this.groups[layout_id]) == 'undefined') {
34742             return false;
34743         }
34744         return this.groups[layout_id] ;
34745     }
34746     
34747     
34748     
34749 });
34750
34751  
34752
34753  /**
34754  *
34755  * This is based on 
34756  * http://masonry.desandro.com
34757  *
34758  * The idea is to render all the bricks based on vertical width...
34759  *
34760  * The original code extends 'outlayer' - we might need to use that....
34761  * 
34762  */
34763
34764
34765 /**
34766  * @class Roo.bootstrap.LayoutMasonryAuto
34767  * @extends Roo.bootstrap.Component
34768  * Bootstrap Layout Masonry class
34769  * 
34770  * @constructor
34771  * Create a new Element
34772  * @param {Object} config The config object
34773  */
34774
34775 Roo.bootstrap.LayoutMasonryAuto = function(config){
34776     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34777 };
34778
34779 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34780     
34781       /**
34782      * @cfg {Boolean} isFitWidth  - resize the width..
34783      */   
34784     isFitWidth : false,  // options..
34785     /**
34786      * @cfg {Boolean} isOriginLeft = left align?
34787      */   
34788     isOriginLeft : true,
34789     /**
34790      * @cfg {Boolean} isOriginTop = top align?
34791      */   
34792     isOriginTop : false,
34793     /**
34794      * @cfg {Boolean} isLayoutInstant = no animation?
34795      */   
34796     isLayoutInstant : false, // needed?
34797     /**
34798      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34799      */   
34800     isResizingContainer : true,
34801     /**
34802      * @cfg {Number} columnWidth  width of the columns 
34803      */   
34804     
34805     columnWidth : 0,
34806     
34807     /**
34808      * @cfg {Number} maxCols maximum number of columns
34809      */   
34810     
34811     maxCols: 0,
34812     /**
34813      * @cfg {Number} padHeight padding below box..
34814      */   
34815     
34816     padHeight : 10, 
34817     
34818     /**
34819      * @cfg {Boolean} isAutoInitial defalut true
34820      */   
34821     
34822     isAutoInitial : true, 
34823     
34824     // private?
34825     gutter : 0,
34826     
34827     containerWidth: 0,
34828     initialColumnWidth : 0,
34829     currentSize : null,
34830     
34831     colYs : null, // array.
34832     maxY : 0,
34833     padWidth: 10,
34834     
34835     
34836     tag: 'div',
34837     cls: '',
34838     bricks: null, //CompositeElement
34839     cols : 0, // array?
34840     // element : null, // wrapped now this.el
34841     _isLayoutInited : null, 
34842     
34843     
34844     getAutoCreate : function(){
34845         
34846         var cfg = {
34847             tag: this.tag,
34848             cls: 'blog-masonary-wrapper ' + this.cls,
34849             cn : {
34850                 cls : 'mas-boxes masonary'
34851             }
34852         };
34853         
34854         return cfg;
34855     },
34856     
34857     getChildContainer: function( )
34858     {
34859         if (this.boxesEl) {
34860             return this.boxesEl;
34861         }
34862         
34863         this.boxesEl = this.el.select('.mas-boxes').first();
34864         
34865         return this.boxesEl;
34866     },
34867     
34868     
34869     initEvents : function()
34870     {
34871         var _this = this;
34872         
34873         if(this.isAutoInitial){
34874             Roo.log('hook children rendered');
34875             this.on('childrenrendered', function() {
34876                 Roo.log('children rendered');
34877                 _this.initial();
34878             } ,this);
34879         }
34880         
34881     },
34882     
34883     initial : function()
34884     {
34885         this.reloadItems();
34886
34887         this.currentSize = this.el.getBox(true);
34888
34889         /// was window resize... - let's see if this works..
34890         Roo.EventManager.onWindowResize(this.resize, this); 
34891
34892         if(!this.isAutoInitial){
34893             this.layout();
34894             return;
34895         }
34896         
34897         this.layout.defer(500,this);
34898     },
34899     
34900     reloadItems: function()
34901     {
34902         this.bricks = this.el.select('.masonry-brick', true);
34903         
34904         this.bricks.each(function(b) {
34905             //Roo.log(b.getSize());
34906             if (!b.attr('originalwidth')) {
34907                 b.attr('originalwidth',  b.getSize().width);
34908             }
34909             
34910         });
34911         
34912         Roo.log(this.bricks.elements.length);
34913     },
34914     
34915     resize : function()
34916     {
34917         Roo.log('resize');
34918         var cs = this.el.getBox(true);
34919         
34920         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34921             Roo.log("no change in with or X");
34922             return;
34923         }
34924         this.currentSize = cs;
34925         this.layout();
34926     },
34927     
34928     layout : function()
34929     {
34930          Roo.log('layout');
34931         this._resetLayout();
34932         //this._manageStamps();
34933       
34934         // don't animate first layout
34935         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34936         this.layoutItems( isInstant );
34937       
34938         // flag for initalized
34939         this._isLayoutInited = true;
34940     },
34941     
34942     layoutItems : function( isInstant )
34943     {
34944         //var items = this._getItemsForLayout( this.items );
34945         // original code supports filtering layout items.. we just ignore it..
34946         
34947         this._layoutItems( this.bricks , isInstant );
34948       
34949         this._postLayout();
34950     },
34951     _layoutItems : function ( items , isInstant)
34952     {
34953        //this.fireEvent( 'layout', this, items );
34954     
34955
34956         if ( !items || !items.elements.length ) {
34957           // no items, emit event with empty array
34958             return;
34959         }
34960
34961         var queue = [];
34962         items.each(function(item) {
34963             Roo.log("layout item");
34964             Roo.log(item);
34965             // get x/y object from method
34966             var position = this._getItemLayoutPosition( item );
34967             // enqueue
34968             position.item = item;
34969             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34970             queue.push( position );
34971         }, this);
34972       
34973         this._processLayoutQueue( queue );
34974     },
34975     /** Sets position of item in DOM
34976     * @param {Element} item
34977     * @param {Number} x - horizontal position
34978     * @param {Number} y - vertical position
34979     * @param {Boolean} isInstant - disables transitions
34980     */
34981     _processLayoutQueue : function( queue )
34982     {
34983         for ( var i=0, len = queue.length; i < len; i++ ) {
34984             var obj = queue[i];
34985             obj.item.position('absolute');
34986             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34987         }
34988     },
34989       
34990     
34991     /**
34992     * Any logic you want to do after each layout,
34993     * i.e. size the container
34994     */
34995     _postLayout : function()
34996     {
34997         this.resizeContainer();
34998     },
34999     
35000     resizeContainer : function()
35001     {
35002         if ( !this.isResizingContainer ) {
35003             return;
35004         }
35005         var size = this._getContainerSize();
35006         if ( size ) {
35007             this.el.setSize(size.width,size.height);
35008             this.boxesEl.setSize(size.width,size.height);
35009         }
35010     },
35011     
35012     
35013     
35014     _resetLayout : function()
35015     {
35016         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35017         this.colWidth = this.el.getWidth();
35018         //this.gutter = this.el.getWidth(); 
35019         
35020         this.measureColumns();
35021
35022         // reset column Y
35023         var i = this.cols;
35024         this.colYs = [];
35025         while (i--) {
35026             this.colYs.push( 0 );
35027         }
35028     
35029         this.maxY = 0;
35030     },
35031
35032     measureColumns : function()
35033     {
35034         this.getContainerWidth();
35035       // if columnWidth is 0, default to outerWidth of first item
35036         if ( !this.columnWidth ) {
35037             var firstItem = this.bricks.first();
35038             Roo.log(firstItem);
35039             this.columnWidth  = this.containerWidth;
35040             if (firstItem && firstItem.attr('originalwidth') ) {
35041                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35042             }
35043             // columnWidth fall back to item of first element
35044             Roo.log("set column width?");
35045                         this.initialColumnWidth = this.columnWidth  ;
35046
35047             // if first elem has no width, default to size of container
35048             
35049         }
35050         
35051         
35052         if (this.initialColumnWidth) {
35053             this.columnWidth = this.initialColumnWidth;
35054         }
35055         
35056         
35057             
35058         // column width is fixed at the top - however if container width get's smaller we should
35059         // reduce it...
35060         
35061         // this bit calcs how man columns..
35062             
35063         var columnWidth = this.columnWidth += this.gutter;
35064       
35065         // calculate columns
35066         var containerWidth = this.containerWidth + this.gutter;
35067         
35068         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35069         // fix rounding errors, typically with gutters
35070         var excess = columnWidth - containerWidth % columnWidth;
35071         
35072         
35073         // if overshoot is less than a pixel, round up, otherwise floor it
35074         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35075         cols = Math[ mathMethod ]( cols );
35076         this.cols = Math.max( cols, 1 );
35077         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35078         
35079          // padding positioning..
35080         var totalColWidth = this.cols * this.columnWidth;
35081         var padavail = this.containerWidth - totalColWidth;
35082         // so for 2 columns - we need 3 'pads'
35083         
35084         var padNeeded = (1+this.cols) * this.padWidth;
35085         
35086         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35087         
35088         this.columnWidth += padExtra
35089         //this.padWidth = Math.floor(padavail /  ( this.cols));
35090         
35091         // adjust colum width so that padding is fixed??
35092         
35093         // we have 3 columns ... total = width * 3
35094         // we have X left over... that should be used by 
35095         
35096         //if (this.expandC) {
35097             
35098         //}
35099         
35100         
35101         
35102     },
35103     
35104     getContainerWidth : function()
35105     {
35106        /* // container is parent if fit width
35107         var container = this.isFitWidth ? this.element.parentNode : this.element;
35108         // check that this.size and size are there
35109         // IE8 triggers resize on body size change, so they might not be
35110         
35111         var size = getSize( container );  //FIXME
35112         this.containerWidth = size && size.innerWidth; //FIXME
35113         */
35114          
35115         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35116         
35117     },
35118     
35119     _getItemLayoutPosition : function( item )  // what is item?
35120     {
35121         // we resize the item to our columnWidth..
35122       
35123         item.setWidth(this.columnWidth);
35124         item.autoBoxAdjust  = false;
35125         
35126         var sz = item.getSize();
35127  
35128         // how many columns does this brick span
35129         var remainder = this.containerWidth % this.columnWidth;
35130         
35131         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35132         // round if off by 1 pixel, otherwise use ceil
35133         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35134         colSpan = Math.min( colSpan, this.cols );
35135         
35136         // normally this should be '1' as we dont' currently allow multi width columns..
35137         
35138         var colGroup = this._getColGroup( colSpan );
35139         // get the minimum Y value from the columns
35140         var minimumY = Math.min.apply( Math, colGroup );
35141         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35142         
35143         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35144          
35145         // position the brick
35146         var position = {
35147             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35148             y: this.currentSize.y + minimumY + this.padHeight
35149         };
35150         
35151         Roo.log(position);
35152         // apply setHeight to necessary columns
35153         var setHeight = minimumY + sz.height + this.padHeight;
35154         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35155         
35156         var setSpan = this.cols + 1 - colGroup.length;
35157         for ( var i = 0; i < setSpan; i++ ) {
35158           this.colYs[ shortColIndex + i ] = setHeight ;
35159         }
35160       
35161         return position;
35162     },
35163     
35164     /**
35165      * @param {Number} colSpan - number of columns the element spans
35166      * @returns {Array} colGroup
35167      */
35168     _getColGroup : function( colSpan )
35169     {
35170         if ( colSpan < 2 ) {
35171           // if brick spans only one column, use all the column Ys
35172           return this.colYs;
35173         }
35174       
35175         var colGroup = [];
35176         // how many different places could this brick fit horizontally
35177         var groupCount = this.cols + 1 - colSpan;
35178         // for each group potential horizontal position
35179         for ( var i = 0; i < groupCount; i++ ) {
35180           // make an array of colY values for that one group
35181           var groupColYs = this.colYs.slice( i, i + colSpan );
35182           // and get the max value of the array
35183           colGroup[i] = Math.max.apply( Math, groupColYs );
35184         }
35185         return colGroup;
35186     },
35187     /*
35188     _manageStamp : function( stamp )
35189     {
35190         var stampSize =  stamp.getSize();
35191         var offset = stamp.getBox();
35192         // get the columns that this stamp affects
35193         var firstX = this.isOriginLeft ? offset.x : offset.right;
35194         var lastX = firstX + stampSize.width;
35195         var firstCol = Math.floor( firstX / this.columnWidth );
35196         firstCol = Math.max( 0, firstCol );
35197         
35198         var lastCol = Math.floor( lastX / this.columnWidth );
35199         // lastCol should not go over if multiple of columnWidth #425
35200         lastCol -= lastX % this.columnWidth ? 0 : 1;
35201         lastCol = Math.min( this.cols - 1, lastCol );
35202         
35203         // set colYs to bottom of the stamp
35204         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35205             stampSize.height;
35206             
35207         for ( var i = firstCol; i <= lastCol; i++ ) {
35208           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35209         }
35210     },
35211     */
35212     
35213     _getContainerSize : function()
35214     {
35215         this.maxY = Math.max.apply( Math, this.colYs );
35216         var size = {
35217             height: this.maxY
35218         };
35219       
35220         if ( this.isFitWidth ) {
35221             size.width = this._getContainerFitWidth();
35222         }
35223       
35224         return size;
35225     },
35226     
35227     _getContainerFitWidth : function()
35228     {
35229         var unusedCols = 0;
35230         // count unused columns
35231         var i = this.cols;
35232         while ( --i ) {
35233           if ( this.colYs[i] !== 0 ) {
35234             break;
35235           }
35236           unusedCols++;
35237         }
35238         // fit container to columns that have been used
35239         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35240     },
35241     
35242     needsResizeLayout : function()
35243     {
35244         var previousWidth = this.containerWidth;
35245         this.getContainerWidth();
35246         return previousWidth !== this.containerWidth;
35247     }
35248  
35249 });
35250
35251  
35252
35253  /*
35254  * - LGPL
35255  *
35256  * element
35257  * 
35258  */
35259
35260 /**
35261  * @class Roo.bootstrap.MasonryBrick
35262  * @extends Roo.bootstrap.Component
35263  * Bootstrap MasonryBrick class
35264  * 
35265  * @constructor
35266  * Create a new MasonryBrick
35267  * @param {Object} config The config object
35268  */
35269
35270 Roo.bootstrap.MasonryBrick = function(config){
35271     
35272     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35273     
35274     Roo.bootstrap.MasonryBrick.register(this);
35275     
35276     this.addEvents({
35277         // raw events
35278         /**
35279          * @event click
35280          * When a MasonryBrick is clcik
35281          * @param {Roo.bootstrap.MasonryBrick} this
35282          * @param {Roo.EventObject} e
35283          */
35284         "click" : true
35285     });
35286 };
35287
35288 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35289     
35290     /**
35291      * @cfg {String} title
35292      */   
35293     title : '',
35294     /**
35295      * @cfg {String} html
35296      */   
35297     html : '',
35298     /**
35299      * @cfg {String} bgimage
35300      */   
35301     bgimage : '',
35302     /**
35303      * @cfg {String} videourl
35304      */   
35305     videourl : '',
35306     /**
35307      * @cfg {String} cls
35308      */   
35309     cls : '',
35310     /**
35311      * @cfg {String} href
35312      */   
35313     href : '',
35314     /**
35315      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35316      */   
35317     size : 'xs',
35318     
35319     /**
35320      * @cfg {String} placetitle (center|bottom)
35321      */   
35322     placetitle : '',
35323     
35324     /**
35325      * @cfg {Boolean} isFitContainer defalut true
35326      */   
35327     isFitContainer : true, 
35328     
35329     /**
35330      * @cfg {Boolean} preventDefault defalut false
35331      */   
35332     preventDefault : false, 
35333     
35334     /**
35335      * @cfg {Boolean} inverse defalut false
35336      */   
35337     maskInverse : false, 
35338     
35339     getAutoCreate : function()
35340     {
35341         if(!this.isFitContainer){
35342             return this.getSplitAutoCreate();
35343         }
35344         
35345         var cls = 'masonry-brick masonry-brick-full';
35346         
35347         if(this.href.length){
35348             cls += ' masonry-brick-link';
35349         }
35350         
35351         if(this.bgimage.length){
35352             cls += ' masonry-brick-image';
35353         }
35354         
35355         if(this.maskInverse){
35356             cls += ' mask-inverse';
35357         }
35358         
35359         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35360             cls += ' enable-mask';
35361         }
35362         
35363         if(this.size){
35364             cls += ' masonry-' + this.size + '-brick';
35365         }
35366         
35367         if(this.placetitle.length){
35368             
35369             switch (this.placetitle) {
35370                 case 'center' :
35371                     cls += ' masonry-center-title';
35372                     break;
35373                 case 'bottom' :
35374                     cls += ' masonry-bottom-title';
35375                     break;
35376                 default:
35377                     break;
35378             }
35379             
35380         } else {
35381             if(!this.html.length && !this.bgimage.length){
35382                 cls += ' masonry-center-title';
35383             }
35384
35385             if(!this.html.length && this.bgimage.length){
35386                 cls += ' masonry-bottom-title';
35387             }
35388         }
35389         
35390         if(this.cls){
35391             cls += ' ' + this.cls;
35392         }
35393         
35394         var cfg = {
35395             tag: (this.href.length) ? 'a' : 'div',
35396             cls: cls,
35397             cn: [
35398                 {
35399                     tag: 'div',
35400                     cls: 'masonry-brick-mask'
35401                 },
35402                 {
35403                     tag: 'div',
35404                     cls: 'masonry-brick-paragraph',
35405                     cn: []
35406                 }
35407             ]
35408         };
35409         
35410         if(this.href.length){
35411             cfg.href = this.href;
35412         }
35413         
35414         var cn = cfg.cn[1].cn;
35415         
35416         if(this.title.length){
35417             cn.push({
35418                 tag: 'h4',
35419                 cls: 'masonry-brick-title',
35420                 html: this.title
35421             });
35422         }
35423         
35424         if(this.html.length){
35425             cn.push({
35426                 tag: 'p',
35427                 cls: 'masonry-brick-text',
35428                 html: this.html
35429             });
35430         }
35431         
35432         if (!this.title.length && !this.html.length) {
35433             cfg.cn[1].cls += ' hide';
35434         }
35435         
35436         if(this.bgimage.length){
35437             cfg.cn.push({
35438                 tag: 'img',
35439                 cls: 'masonry-brick-image-view',
35440                 src: this.bgimage
35441             });
35442         }
35443         
35444         if(this.videourl.length){
35445             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35446             // youtube support only?
35447             cfg.cn.push({
35448                 tag: 'iframe',
35449                 cls: 'masonry-brick-image-view',
35450                 src: vurl,
35451                 frameborder : 0,
35452                 allowfullscreen : true
35453             });
35454         }
35455         
35456         return cfg;
35457         
35458     },
35459     
35460     getSplitAutoCreate : function()
35461     {
35462         var cls = 'masonry-brick masonry-brick-split';
35463         
35464         if(this.href.length){
35465             cls += ' masonry-brick-link';
35466         }
35467         
35468         if(this.bgimage.length){
35469             cls += ' masonry-brick-image';
35470         }
35471         
35472         if(this.size){
35473             cls += ' masonry-' + this.size + '-brick';
35474         }
35475         
35476         switch (this.placetitle) {
35477             case 'center' :
35478                 cls += ' masonry-center-title';
35479                 break;
35480             case 'bottom' :
35481                 cls += ' masonry-bottom-title';
35482                 break;
35483             default:
35484                 if(!this.bgimage.length){
35485                     cls += ' masonry-center-title';
35486                 }
35487
35488                 if(this.bgimage.length){
35489                     cls += ' masonry-bottom-title';
35490                 }
35491                 break;
35492         }
35493         
35494         if(this.cls){
35495             cls += ' ' + this.cls;
35496         }
35497         
35498         var cfg = {
35499             tag: (this.href.length) ? 'a' : 'div',
35500             cls: cls,
35501             cn: [
35502                 {
35503                     tag: 'div',
35504                     cls: 'masonry-brick-split-head',
35505                     cn: [
35506                         {
35507                             tag: 'div',
35508                             cls: 'masonry-brick-paragraph',
35509                             cn: []
35510                         }
35511                     ]
35512                 },
35513                 {
35514                     tag: 'div',
35515                     cls: 'masonry-brick-split-body',
35516                     cn: []
35517                 }
35518             ]
35519         };
35520         
35521         if(this.href.length){
35522             cfg.href = this.href;
35523         }
35524         
35525         if(this.title.length){
35526             cfg.cn[0].cn[0].cn.push({
35527                 tag: 'h4',
35528                 cls: 'masonry-brick-title',
35529                 html: this.title
35530             });
35531         }
35532         
35533         if(this.html.length){
35534             cfg.cn[1].cn.push({
35535                 tag: 'p',
35536                 cls: 'masonry-brick-text',
35537                 html: this.html
35538             });
35539         }
35540
35541         if(this.bgimage.length){
35542             cfg.cn[0].cn.push({
35543                 tag: 'img',
35544                 cls: 'masonry-brick-image-view',
35545                 src: this.bgimage
35546             });
35547         }
35548         
35549         if(this.videourl.length){
35550             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35551             // youtube support only?
35552             cfg.cn[0].cn.cn.push({
35553                 tag: 'iframe',
35554                 cls: 'masonry-brick-image-view',
35555                 src: vurl,
35556                 frameborder : 0,
35557                 allowfullscreen : true
35558             });
35559         }
35560         
35561         return cfg;
35562     },
35563     
35564     initEvents: function() 
35565     {
35566         switch (this.size) {
35567             case 'xs' :
35568                 this.x = 1;
35569                 this.y = 1;
35570                 break;
35571             case 'sm' :
35572                 this.x = 2;
35573                 this.y = 2;
35574                 break;
35575             case 'md' :
35576             case 'md-left' :
35577             case 'md-right' :
35578                 this.x = 3;
35579                 this.y = 3;
35580                 break;
35581             case 'tall' :
35582                 this.x = 2;
35583                 this.y = 3;
35584                 break;
35585             case 'wide' :
35586                 this.x = 3;
35587                 this.y = 2;
35588                 break;
35589             case 'wide-thin' :
35590                 this.x = 3;
35591                 this.y = 1;
35592                 break;
35593                         
35594             default :
35595                 break;
35596         }
35597         
35598         if(Roo.isTouch){
35599             this.el.on('touchstart', this.onTouchStart, this);
35600             this.el.on('touchmove', this.onTouchMove, this);
35601             this.el.on('touchend', this.onTouchEnd, this);
35602             this.el.on('contextmenu', this.onContextMenu, this);
35603         } else {
35604             this.el.on('mouseenter'  ,this.enter, this);
35605             this.el.on('mouseleave', this.leave, this);
35606             this.el.on('click', this.onClick, this);
35607         }
35608         
35609         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35610             this.parent().bricks.push(this);   
35611         }
35612         
35613     },
35614     
35615     onClick: function(e, el)
35616     {
35617         var time = this.endTimer - this.startTimer;
35618         // Roo.log(e.preventDefault());
35619         if(Roo.isTouch){
35620             if(time > 1000){
35621                 e.preventDefault();
35622                 return;
35623             }
35624         }
35625         
35626         if(!this.preventDefault){
35627             return;
35628         }
35629         
35630         e.preventDefault();
35631         
35632         if (this.activeClass != '') {
35633             this.selectBrick();
35634         }
35635         
35636         this.fireEvent('click', this, e);
35637     },
35638     
35639     enter: function(e, el)
35640     {
35641         e.preventDefault();
35642         
35643         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35644             return;
35645         }
35646         
35647         if(this.bgimage.length && this.html.length){
35648             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35649         }
35650     },
35651     
35652     leave: function(e, el)
35653     {
35654         e.preventDefault();
35655         
35656         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35657             return;
35658         }
35659         
35660         if(this.bgimage.length && this.html.length){
35661             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35662         }
35663     },
35664     
35665     onTouchStart: function(e, el)
35666     {
35667 //        e.preventDefault();
35668         
35669         this.touchmoved = false;
35670         
35671         if(!this.isFitContainer){
35672             return;
35673         }
35674         
35675         if(!this.bgimage.length || !this.html.length){
35676             return;
35677         }
35678         
35679         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35680         
35681         this.timer = new Date().getTime();
35682         
35683     },
35684     
35685     onTouchMove: function(e, el)
35686     {
35687         this.touchmoved = true;
35688     },
35689     
35690     onContextMenu : function(e,el)
35691     {
35692         e.preventDefault();
35693         e.stopPropagation();
35694         return false;
35695     },
35696     
35697     onTouchEnd: function(e, el)
35698     {
35699 //        e.preventDefault();
35700         
35701         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35702         
35703             this.leave(e,el);
35704             
35705             return;
35706         }
35707         
35708         if(!this.bgimage.length || !this.html.length){
35709             
35710             if(this.href.length){
35711                 window.location.href = this.href;
35712             }
35713             
35714             return;
35715         }
35716         
35717         if(!this.isFitContainer){
35718             return;
35719         }
35720         
35721         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35722         
35723         window.location.href = this.href;
35724     },
35725     
35726     //selection on single brick only
35727     selectBrick : function() {
35728         
35729         if (!this.parentId) {
35730             return;
35731         }
35732         
35733         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35734         var index = m.selectedBrick.indexOf(this.id);
35735         
35736         if ( index > -1) {
35737             m.selectedBrick.splice(index,1);
35738             this.el.removeClass(this.activeClass);
35739             return;
35740         }
35741         
35742         for(var i = 0; i < m.selectedBrick.length; i++) {
35743             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35744             b.el.removeClass(b.activeClass);
35745         }
35746         
35747         m.selectedBrick = [];
35748         
35749         m.selectedBrick.push(this.id);
35750         this.el.addClass(this.activeClass);
35751         return;
35752     },
35753     
35754     isSelected : function(){
35755         return this.el.hasClass(this.activeClass);
35756         
35757     }
35758 });
35759
35760 Roo.apply(Roo.bootstrap.MasonryBrick, {
35761     
35762     //groups: {},
35763     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35764      /**
35765     * register a Masonry Brick
35766     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35767     */
35768     
35769     register : function(brick)
35770     {
35771         //this.groups[brick.id] = brick;
35772         this.groups.add(brick.id, brick);
35773     },
35774     /**
35775     * fetch a  masonry brick based on the masonry brick ID
35776     * @param {string} the masonry brick to add
35777     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35778     */
35779     
35780     get: function(brick_id) 
35781     {
35782         // if (typeof(this.groups[brick_id]) == 'undefined') {
35783         //     return false;
35784         // }
35785         // return this.groups[brick_id] ;
35786         
35787         if(this.groups.key(brick_id)) {
35788             return this.groups.key(brick_id);
35789         }
35790         
35791         return false;
35792     }
35793     
35794     
35795     
35796 });
35797
35798  /*
35799  * - LGPL
35800  *
35801  * element
35802  * 
35803  */
35804
35805 /**
35806  * @class Roo.bootstrap.Brick
35807  * @extends Roo.bootstrap.Component
35808  * Bootstrap Brick class
35809  * 
35810  * @constructor
35811  * Create a new Brick
35812  * @param {Object} config The config object
35813  */
35814
35815 Roo.bootstrap.Brick = function(config){
35816     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35817     
35818     this.addEvents({
35819         // raw events
35820         /**
35821          * @event click
35822          * When a Brick is click
35823          * @param {Roo.bootstrap.Brick} this
35824          * @param {Roo.EventObject} e
35825          */
35826         "click" : true
35827     });
35828 };
35829
35830 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35831     
35832     /**
35833      * @cfg {String} title
35834      */   
35835     title : '',
35836     /**
35837      * @cfg {String} html
35838      */   
35839     html : '',
35840     /**
35841      * @cfg {String} bgimage
35842      */   
35843     bgimage : '',
35844     /**
35845      * @cfg {String} cls
35846      */   
35847     cls : '',
35848     /**
35849      * @cfg {String} href
35850      */   
35851     href : '',
35852     /**
35853      * @cfg {String} video
35854      */   
35855     video : '',
35856     /**
35857      * @cfg {Boolean} square
35858      */   
35859     square : true,
35860     
35861     getAutoCreate : function()
35862     {
35863         var cls = 'roo-brick';
35864         
35865         if(this.href.length){
35866             cls += ' roo-brick-link';
35867         }
35868         
35869         if(this.bgimage.length){
35870             cls += ' roo-brick-image';
35871         }
35872         
35873         if(!this.html.length && !this.bgimage.length){
35874             cls += ' roo-brick-center-title';
35875         }
35876         
35877         if(!this.html.length && this.bgimage.length){
35878             cls += ' roo-brick-bottom-title';
35879         }
35880         
35881         if(this.cls){
35882             cls += ' ' + this.cls;
35883         }
35884         
35885         var cfg = {
35886             tag: (this.href.length) ? 'a' : 'div',
35887             cls: cls,
35888             cn: [
35889                 {
35890                     tag: 'div',
35891                     cls: 'roo-brick-paragraph',
35892                     cn: []
35893                 }
35894             ]
35895         };
35896         
35897         if(this.href.length){
35898             cfg.href = this.href;
35899         }
35900         
35901         var cn = cfg.cn[0].cn;
35902         
35903         if(this.title.length){
35904             cn.push({
35905                 tag: 'h4',
35906                 cls: 'roo-brick-title',
35907                 html: this.title
35908             });
35909         }
35910         
35911         if(this.html.length){
35912             cn.push({
35913                 tag: 'p',
35914                 cls: 'roo-brick-text',
35915                 html: this.html
35916             });
35917         } else {
35918             cn.cls += ' hide';
35919         }
35920         
35921         if(this.bgimage.length){
35922             cfg.cn.push({
35923                 tag: 'img',
35924                 cls: 'roo-brick-image-view',
35925                 src: this.bgimage
35926             });
35927         }
35928         
35929         return cfg;
35930     },
35931     
35932     initEvents: function() 
35933     {
35934         if(this.title.length || this.html.length){
35935             this.el.on('mouseenter'  ,this.enter, this);
35936             this.el.on('mouseleave', this.leave, this);
35937         }
35938         
35939         Roo.EventManager.onWindowResize(this.resize, this); 
35940         
35941         if(this.bgimage.length){
35942             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35943             this.imageEl.on('load', this.onImageLoad, this);
35944             return;
35945         }
35946         
35947         this.resize();
35948     },
35949     
35950     onImageLoad : function()
35951     {
35952         this.resize();
35953     },
35954     
35955     resize : function()
35956     {
35957         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35958         
35959         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35960         
35961         if(this.bgimage.length){
35962             var image = this.el.select('.roo-brick-image-view', true).first();
35963             
35964             image.setWidth(paragraph.getWidth());
35965             
35966             if(this.square){
35967                 image.setHeight(paragraph.getWidth());
35968             }
35969             
35970             this.el.setHeight(image.getHeight());
35971             paragraph.setHeight(image.getHeight());
35972             
35973         }
35974         
35975     },
35976     
35977     enter: function(e, el)
35978     {
35979         e.preventDefault();
35980         
35981         if(this.bgimage.length){
35982             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35983             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35984         }
35985     },
35986     
35987     leave: function(e, el)
35988     {
35989         e.preventDefault();
35990         
35991         if(this.bgimage.length){
35992             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35993             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35994         }
35995     }
35996     
35997 });
35998
35999  
36000
36001  /*
36002  * - LGPL
36003  *
36004  * Number field 
36005  */
36006
36007 /**
36008  * @class Roo.bootstrap.NumberField
36009  * @extends Roo.bootstrap.Input
36010  * Bootstrap NumberField class
36011  * 
36012  * 
36013  * 
36014  * 
36015  * @constructor
36016  * Create a new NumberField
36017  * @param {Object} config The config object
36018  */
36019
36020 Roo.bootstrap.NumberField = function(config){
36021     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36022 };
36023
36024 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36025     
36026     /**
36027      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36028      */
36029     allowDecimals : true,
36030     /**
36031      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36032      */
36033     decimalSeparator : ".",
36034     /**
36035      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36036      */
36037     decimalPrecision : 2,
36038     /**
36039      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36040      */
36041     allowNegative : true,
36042     
36043     /**
36044      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36045      */
36046     allowZero: true,
36047     /**
36048      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36049      */
36050     minValue : Number.NEGATIVE_INFINITY,
36051     /**
36052      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36053      */
36054     maxValue : Number.MAX_VALUE,
36055     /**
36056      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36057      */
36058     minText : "The minimum value for this field is {0}",
36059     /**
36060      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36061      */
36062     maxText : "The maximum value for this field is {0}",
36063     /**
36064      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36065      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36066      */
36067     nanText : "{0} is not a valid number",
36068     /**
36069      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36070      */
36071     thousandsDelimiter : false,
36072     /**
36073      * @cfg {String} valueAlign alignment of value
36074      */
36075     valueAlign : "left",
36076
36077     getAutoCreate : function()
36078     {
36079         var hiddenInput = {
36080             tag: 'input',
36081             type: 'hidden',
36082             id: Roo.id(),
36083             cls: 'hidden-number-input'
36084         };
36085         
36086         if (this.name) {
36087             hiddenInput.name = this.name;
36088         }
36089         
36090         this.name = '';
36091         
36092         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36093         
36094         this.name = hiddenInput.name;
36095         
36096         if(cfg.cn.length > 0) {
36097             cfg.cn.push(hiddenInput);
36098         }
36099         
36100         return cfg;
36101     },
36102
36103     // private
36104     initEvents : function()
36105     {   
36106         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36107         
36108         var allowed = "0123456789";
36109         
36110         if(this.allowDecimals){
36111             allowed += this.decimalSeparator;
36112         }
36113         
36114         if(this.allowNegative){
36115             allowed += "-";
36116         }
36117         
36118         if(this.thousandsDelimiter) {
36119             allowed += ",";
36120         }
36121         
36122         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36123         
36124         var keyPress = function(e){
36125             
36126             var k = e.getKey();
36127             
36128             var c = e.getCharCode();
36129             
36130             if(
36131                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36132                     allowed.indexOf(String.fromCharCode(c)) === -1
36133             ){
36134                 e.stopEvent();
36135                 return;
36136             }
36137             
36138             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36139                 return;
36140             }
36141             
36142             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36143                 e.stopEvent();
36144             }
36145         };
36146         
36147         this.el.on("keypress", keyPress, this);
36148     },
36149     
36150     validateValue : function(value)
36151     {
36152         
36153         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36154             return false;
36155         }
36156         
36157         var num = this.parseValue(value);
36158         
36159         if(isNaN(num)){
36160             this.markInvalid(String.format(this.nanText, value));
36161             return false;
36162         }
36163         
36164         if(num < this.minValue){
36165             this.markInvalid(String.format(this.minText, this.minValue));
36166             return false;
36167         }
36168         
36169         if(num > this.maxValue){
36170             this.markInvalid(String.format(this.maxText, this.maxValue));
36171             return false;
36172         }
36173         
36174         return true;
36175     },
36176
36177     getValue : function()
36178     {
36179         var v = this.hiddenEl().getValue();
36180         
36181         return this.fixPrecision(this.parseValue(v));
36182     },
36183
36184     parseValue : function(value)
36185     {
36186         if(this.thousandsDelimiter) {
36187             value += "";
36188             r = new RegExp(",", "g");
36189             value = value.replace(r, "");
36190         }
36191         
36192         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36193         return isNaN(value) ? '' : value;
36194     },
36195
36196     fixPrecision : function(value)
36197     {
36198         if(this.thousandsDelimiter) {
36199             value += "";
36200             r = new RegExp(",", "g");
36201             value = value.replace(r, "");
36202         }
36203         
36204         var nan = isNaN(value);
36205         
36206         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36207             return nan ? '' : value;
36208         }
36209         return parseFloat(value).toFixed(this.decimalPrecision);
36210     },
36211
36212     setValue : function(v)
36213     {
36214         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36215         
36216         this.value = v;
36217         
36218         if(this.rendered){
36219             
36220             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36221             
36222             this.inputEl().dom.value = (v == '') ? '' :
36223                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36224             
36225             if(!this.allowZero && v === '0') {
36226                 this.hiddenEl().dom.value = '';
36227                 this.inputEl().dom.value = '';
36228             }
36229             
36230             this.validate();
36231         }
36232     },
36233
36234     decimalPrecisionFcn : function(v)
36235     {
36236         return Math.floor(v);
36237     },
36238
36239     beforeBlur : function()
36240     {
36241         var v = this.parseValue(this.getRawValue());
36242         
36243         if(v || v === 0 || v === ''){
36244             this.setValue(v);
36245         }
36246     },
36247     
36248     hiddenEl : function()
36249     {
36250         return this.el.select('input.hidden-number-input',true).first();
36251     }
36252     
36253 });
36254
36255  
36256
36257 /*
36258 * Licence: LGPL
36259 */
36260
36261 /**
36262  * @class Roo.bootstrap.DocumentSlider
36263  * @extends Roo.bootstrap.Component
36264  * Bootstrap DocumentSlider class
36265  * 
36266  * @constructor
36267  * Create a new DocumentViewer
36268  * @param {Object} config The config object
36269  */
36270
36271 Roo.bootstrap.DocumentSlider = function(config){
36272     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36273     
36274     this.files = [];
36275     
36276     this.addEvents({
36277         /**
36278          * @event initial
36279          * Fire after initEvent
36280          * @param {Roo.bootstrap.DocumentSlider} this
36281          */
36282         "initial" : true,
36283         /**
36284          * @event update
36285          * Fire after update
36286          * @param {Roo.bootstrap.DocumentSlider} this
36287          */
36288         "update" : true,
36289         /**
36290          * @event click
36291          * Fire after click
36292          * @param {Roo.bootstrap.DocumentSlider} this
36293          */
36294         "click" : true
36295     });
36296 };
36297
36298 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36299     
36300     files : false,
36301     
36302     indicator : 0,
36303     
36304     getAutoCreate : function()
36305     {
36306         var cfg = {
36307             tag : 'div',
36308             cls : 'roo-document-slider',
36309             cn : [
36310                 {
36311                     tag : 'div',
36312                     cls : 'roo-document-slider-header',
36313                     cn : [
36314                         {
36315                             tag : 'div',
36316                             cls : 'roo-document-slider-header-title'
36317                         }
36318                     ]
36319                 },
36320                 {
36321                     tag : 'div',
36322                     cls : 'roo-document-slider-body',
36323                     cn : [
36324                         {
36325                             tag : 'div',
36326                             cls : 'roo-document-slider-prev',
36327                             cn : [
36328                                 {
36329                                     tag : 'i',
36330                                     cls : 'fa fa-chevron-left'
36331                                 }
36332                             ]
36333                         },
36334                         {
36335                             tag : 'div',
36336                             cls : 'roo-document-slider-thumb',
36337                             cn : [
36338                                 {
36339                                     tag : 'img',
36340                                     cls : 'roo-document-slider-image'
36341                                 }
36342                             ]
36343                         },
36344                         {
36345                             tag : 'div',
36346                             cls : 'roo-document-slider-next',
36347                             cn : [
36348                                 {
36349                                     tag : 'i',
36350                                     cls : 'fa fa-chevron-right'
36351                                 }
36352                             ]
36353                         }
36354                     ]
36355                 }
36356             ]
36357         };
36358         
36359         return cfg;
36360     },
36361     
36362     initEvents : function()
36363     {
36364         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36365         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36366         
36367         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36368         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36369         
36370         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36371         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36372         
36373         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36374         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36375         
36376         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36377         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36378         
36379         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36380         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36381         
36382         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36383         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36384         
36385         this.thumbEl.on('click', this.onClick, this);
36386         
36387         this.prevIndicator.on('click', this.prev, this);
36388         
36389         this.nextIndicator.on('click', this.next, this);
36390         
36391     },
36392     
36393     initial : function()
36394     {
36395         if(this.files.length){
36396             this.indicator = 1;
36397             this.update()
36398         }
36399         
36400         this.fireEvent('initial', this);
36401     },
36402     
36403     update : function()
36404     {
36405         this.imageEl.attr('src', this.files[this.indicator - 1]);
36406         
36407         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36408         
36409         this.prevIndicator.show();
36410         
36411         if(this.indicator == 1){
36412             this.prevIndicator.hide();
36413         }
36414         
36415         this.nextIndicator.show();
36416         
36417         if(this.indicator == this.files.length){
36418             this.nextIndicator.hide();
36419         }
36420         
36421         this.thumbEl.scrollTo('top');
36422         
36423         this.fireEvent('update', this);
36424     },
36425     
36426     onClick : function(e)
36427     {
36428         e.preventDefault();
36429         
36430         this.fireEvent('click', this);
36431     },
36432     
36433     prev : function(e)
36434     {
36435         e.preventDefault();
36436         
36437         this.indicator = Math.max(1, this.indicator - 1);
36438         
36439         this.update();
36440     },
36441     
36442     next : function(e)
36443     {
36444         e.preventDefault();
36445         
36446         this.indicator = Math.min(this.files.length, this.indicator + 1);
36447         
36448         this.update();
36449     }
36450 });
36451 /*
36452  * - LGPL
36453  *
36454  * RadioSet
36455  *
36456  *
36457  */
36458
36459 /**
36460  * @class Roo.bootstrap.RadioSet
36461  * @extends Roo.bootstrap.Input
36462  * Bootstrap RadioSet class
36463  * @cfg {String} indicatorpos (left|right) default left
36464  * @cfg {Boolean} inline (true|false) inline the element (default true)
36465  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36466  * @constructor
36467  * Create a new RadioSet
36468  * @param {Object} config The config object
36469  */
36470
36471 Roo.bootstrap.RadioSet = function(config){
36472     
36473     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36474     
36475     this.radioes = [];
36476     
36477     Roo.bootstrap.RadioSet.register(this);
36478     
36479     this.addEvents({
36480         /**
36481         * @event check
36482         * Fires when the element is checked or unchecked.
36483         * @param {Roo.bootstrap.RadioSet} this This radio
36484         * @param {Roo.bootstrap.Radio} item The checked item
36485         */
36486        check : true,
36487        /**
36488         * @event click
36489         * Fires when the element is click.
36490         * @param {Roo.bootstrap.RadioSet} this This radio set
36491         * @param {Roo.bootstrap.Radio} item The checked item
36492         * @param {Roo.EventObject} e The event object
36493         */
36494        click : true
36495     });
36496     
36497 };
36498
36499 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36500
36501     radioes : false,
36502     
36503     inline : true,
36504     
36505     weight : '',
36506     
36507     indicatorpos : 'left',
36508     
36509     getAutoCreate : function()
36510     {
36511         var label = {
36512             tag : 'label',
36513             cls : 'roo-radio-set-label',
36514             cn : [
36515                 {
36516                     tag : 'span',
36517                     html : this.fieldLabel
36518                 }
36519             ]
36520         };
36521         if (Roo.bootstrap.version == 3) {
36522             
36523             
36524             if(this.indicatorpos == 'left'){
36525                 label.cn.unshift({
36526                     tag : 'i',
36527                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36528                     tooltip : 'This field is required'
36529                 });
36530             } else {
36531                 label.cn.push({
36532                     tag : 'i',
36533                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36534                     tooltip : 'This field is required'
36535                 });
36536             }
36537         }
36538         var items = {
36539             tag : 'div',
36540             cls : 'roo-radio-set-items'
36541         };
36542         
36543         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36544         
36545         if (align === 'left' && this.fieldLabel.length) {
36546             
36547             items = {
36548                 cls : "roo-radio-set-right", 
36549                 cn: [
36550                     items
36551                 ]
36552             };
36553             
36554             if(this.labelWidth > 12){
36555                 label.style = "width: " + this.labelWidth + 'px';
36556             }
36557             
36558             if(this.labelWidth < 13 && this.labelmd == 0){
36559                 this.labelmd = this.labelWidth;
36560             }
36561             
36562             if(this.labellg > 0){
36563                 label.cls += ' col-lg-' + this.labellg;
36564                 items.cls += ' col-lg-' + (12 - this.labellg);
36565             }
36566             
36567             if(this.labelmd > 0){
36568                 label.cls += ' col-md-' + this.labelmd;
36569                 items.cls += ' col-md-' + (12 - this.labelmd);
36570             }
36571             
36572             if(this.labelsm > 0){
36573                 label.cls += ' col-sm-' + this.labelsm;
36574                 items.cls += ' col-sm-' + (12 - this.labelsm);
36575             }
36576             
36577             if(this.labelxs > 0){
36578                 label.cls += ' col-xs-' + this.labelxs;
36579                 items.cls += ' col-xs-' + (12 - this.labelxs);
36580             }
36581         }
36582         
36583         var cfg = {
36584             tag : 'div',
36585             cls : 'roo-radio-set',
36586             cn : [
36587                 {
36588                     tag : 'input',
36589                     cls : 'roo-radio-set-input',
36590                     type : 'hidden',
36591                     name : this.name,
36592                     value : this.value ? this.value :  ''
36593                 },
36594                 label,
36595                 items
36596             ]
36597         };
36598         
36599         if(this.weight.length){
36600             cfg.cls += ' roo-radio-' + this.weight;
36601         }
36602         
36603         if(this.inline) {
36604             cfg.cls += ' roo-radio-set-inline';
36605         }
36606         
36607         var settings=this;
36608         ['xs','sm','md','lg'].map(function(size){
36609             if (settings[size]) {
36610                 cfg.cls += ' col-' + size + '-' + settings[size];
36611             }
36612         });
36613         
36614         return cfg;
36615         
36616     },
36617
36618     initEvents : function()
36619     {
36620         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36621         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36622         
36623         if(!this.fieldLabel.length){
36624             this.labelEl.hide();
36625         }
36626         
36627         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36628         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36629         
36630         this.indicator = this.indicatorEl();
36631         
36632         if(this.indicator){
36633             this.indicator.addClass('invisible');
36634         }
36635         
36636         this.originalValue = this.getValue();
36637         
36638     },
36639     
36640     inputEl: function ()
36641     {
36642         return this.el.select('.roo-radio-set-input', true).first();
36643     },
36644     
36645     getChildContainer : function()
36646     {
36647         return this.itemsEl;
36648     },
36649     
36650     register : function(item)
36651     {
36652         this.radioes.push(item);
36653         
36654     },
36655     
36656     validate : function()
36657     {   
36658         if(this.getVisibilityEl().hasClass('hidden')){
36659             return true;
36660         }
36661         
36662         var valid = false;
36663         
36664         Roo.each(this.radioes, function(i){
36665             if(!i.checked){
36666                 return;
36667             }
36668             
36669             valid = true;
36670             return false;
36671         });
36672         
36673         if(this.allowBlank) {
36674             return true;
36675         }
36676         
36677         if(this.disabled || valid){
36678             this.markValid();
36679             return true;
36680         }
36681         
36682         this.markInvalid();
36683         return false;
36684         
36685     },
36686     
36687     markValid : function()
36688     {
36689         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36690             this.indicatorEl().removeClass('visible');
36691             this.indicatorEl().addClass('invisible');
36692         }
36693         
36694         
36695         if (Roo.bootstrap.version == 3) {
36696             this.el.removeClass([this.invalidClass, this.validClass]);
36697             this.el.addClass(this.validClass);
36698         } else {
36699             this.el.removeClass(['is-invalid','is-valid']);
36700             this.el.addClass(['is-valid']);
36701         }
36702         this.fireEvent('valid', this);
36703     },
36704     
36705     markInvalid : function(msg)
36706     {
36707         if(this.allowBlank || this.disabled){
36708             return;
36709         }
36710         
36711         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36712             this.indicatorEl().removeClass('invisible');
36713             this.indicatorEl().addClass('visible');
36714         }
36715         if (Roo.bootstrap.version == 3) {
36716             this.el.removeClass([this.invalidClass, this.validClass]);
36717             this.el.addClass(this.invalidClass);
36718         } else {
36719             this.el.removeClass(['is-invalid','is-valid']);
36720             this.el.addClass(['is-invalid']);
36721         }
36722         
36723         this.fireEvent('invalid', this, msg);
36724         
36725     },
36726     
36727     setValue : function(v, suppressEvent)
36728     {   
36729         if(this.value === v){
36730             return;
36731         }
36732         
36733         this.value = v;
36734         
36735         if(this.rendered){
36736             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36737         }
36738         
36739         Roo.each(this.radioes, function(i){
36740             i.checked = false;
36741             i.el.removeClass('checked');
36742         });
36743         
36744         Roo.each(this.radioes, function(i){
36745             
36746             if(i.value === v || i.value.toString() === v.toString()){
36747                 i.checked = true;
36748                 i.el.addClass('checked');
36749                 
36750                 if(suppressEvent !== true){
36751                     this.fireEvent('check', this, i);
36752                 }
36753                 
36754                 return false;
36755             }
36756             
36757         }, this);
36758         
36759         this.validate();
36760     },
36761     
36762     clearInvalid : function(){
36763         
36764         if(!this.el || this.preventMark){
36765             return;
36766         }
36767         
36768         this.el.removeClass([this.invalidClass]);
36769         
36770         this.fireEvent('valid', this);
36771     }
36772     
36773 });
36774
36775 Roo.apply(Roo.bootstrap.RadioSet, {
36776     
36777     groups: {},
36778     
36779     register : function(set)
36780     {
36781         this.groups[set.name] = set;
36782     },
36783     
36784     get: function(name) 
36785     {
36786         if (typeof(this.groups[name]) == 'undefined') {
36787             return false;
36788         }
36789         
36790         return this.groups[name] ;
36791     }
36792     
36793 });
36794 /*
36795  * Based on:
36796  * Ext JS Library 1.1.1
36797  * Copyright(c) 2006-2007, Ext JS, LLC.
36798  *
36799  * Originally Released Under LGPL - original licence link has changed is not relivant.
36800  *
36801  * Fork - LGPL
36802  * <script type="text/javascript">
36803  */
36804
36805
36806 /**
36807  * @class Roo.bootstrap.SplitBar
36808  * @extends Roo.util.Observable
36809  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36810  * <br><br>
36811  * Usage:
36812  * <pre><code>
36813 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36814                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36815 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36816 split.minSize = 100;
36817 split.maxSize = 600;
36818 split.animate = true;
36819 split.on('moved', splitterMoved);
36820 </code></pre>
36821  * @constructor
36822  * Create a new SplitBar
36823  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36824  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36825  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36826  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36827                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36828                         position of the SplitBar).
36829  */
36830 Roo.bootstrap.SplitBar = function(cfg){
36831     
36832     /** @private */
36833     
36834     //{
36835     //  dragElement : elm
36836     //  resizingElement: el,
36837         // optional..
36838     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36839     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36840         // existingProxy ???
36841     //}
36842     
36843     this.el = Roo.get(cfg.dragElement, true);
36844     this.el.dom.unselectable = "on";
36845     /** @private */
36846     this.resizingEl = Roo.get(cfg.resizingElement, true);
36847
36848     /**
36849      * @private
36850      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36851      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36852      * @type Number
36853      */
36854     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36855     
36856     /**
36857      * The minimum size of the resizing element. (Defaults to 0)
36858      * @type Number
36859      */
36860     this.minSize = 0;
36861     
36862     /**
36863      * The maximum size of the resizing element. (Defaults to 2000)
36864      * @type Number
36865      */
36866     this.maxSize = 2000;
36867     
36868     /**
36869      * Whether to animate the transition to the new size
36870      * @type Boolean
36871      */
36872     this.animate = false;
36873     
36874     /**
36875      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36876      * @type Boolean
36877      */
36878     this.useShim = false;
36879     
36880     /** @private */
36881     this.shim = null;
36882     
36883     if(!cfg.existingProxy){
36884         /** @private */
36885         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36886     }else{
36887         this.proxy = Roo.get(cfg.existingProxy).dom;
36888     }
36889     /** @private */
36890     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36891     
36892     /** @private */
36893     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36894     
36895     /** @private */
36896     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36897     
36898     /** @private */
36899     this.dragSpecs = {};
36900     
36901     /**
36902      * @private The adapter to use to positon and resize elements
36903      */
36904     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36905     this.adapter.init(this);
36906     
36907     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36908         /** @private */
36909         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36910         this.el.addClass("roo-splitbar-h");
36911     }else{
36912         /** @private */
36913         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36914         this.el.addClass("roo-splitbar-v");
36915     }
36916     
36917     this.addEvents({
36918         /**
36919          * @event resize
36920          * Fires when the splitter is moved (alias for {@link #event-moved})
36921          * @param {Roo.bootstrap.SplitBar} this
36922          * @param {Number} newSize the new width or height
36923          */
36924         "resize" : true,
36925         /**
36926          * @event moved
36927          * Fires when the splitter is moved
36928          * @param {Roo.bootstrap.SplitBar} this
36929          * @param {Number} newSize the new width or height
36930          */
36931         "moved" : true,
36932         /**
36933          * @event beforeresize
36934          * Fires before the splitter is dragged
36935          * @param {Roo.bootstrap.SplitBar} this
36936          */
36937         "beforeresize" : true,
36938
36939         "beforeapply" : true
36940     });
36941
36942     Roo.util.Observable.call(this);
36943 };
36944
36945 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36946     onStartProxyDrag : function(x, y){
36947         this.fireEvent("beforeresize", this);
36948         if(!this.overlay){
36949             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36950             o.unselectable();
36951             o.enableDisplayMode("block");
36952             // all splitbars share the same overlay
36953             Roo.bootstrap.SplitBar.prototype.overlay = o;
36954         }
36955         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36956         this.overlay.show();
36957         Roo.get(this.proxy).setDisplayed("block");
36958         var size = this.adapter.getElementSize(this);
36959         this.activeMinSize = this.getMinimumSize();;
36960         this.activeMaxSize = this.getMaximumSize();;
36961         var c1 = size - this.activeMinSize;
36962         var c2 = Math.max(this.activeMaxSize - size, 0);
36963         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36964             this.dd.resetConstraints();
36965             this.dd.setXConstraint(
36966                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36967                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36968             );
36969             this.dd.setYConstraint(0, 0);
36970         }else{
36971             this.dd.resetConstraints();
36972             this.dd.setXConstraint(0, 0);
36973             this.dd.setYConstraint(
36974                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
36975                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36976             );
36977          }
36978         this.dragSpecs.startSize = size;
36979         this.dragSpecs.startPoint = [x, y];
36980         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36981     },
36982     
36983     /** 
36984      * @private Called after the drag operation by the DDProxy
36985      */
36986     onEndProxyDrag : function(e){
36987         Roo.get(this.proxy).setDisplayed(false);
36988         var endPoint = Roo.lib.Event.getXY(e);
36989         if(this.overlay){
36990             this.overlay.hide();
36991         }
36992         var newSize;
36993         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36994             newSize = this.dragSpecs.startSize + 
36995                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36996                     endPoint[0] - this.dragSpecs.startPoint[0] :
36997                     this.dragSpecs.startPoint[0] - endPoint[0]
36998                 );
36999         }else{
37000             newSize = this.dragSpecs.startSize + 
37001                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37002                     endPoint[1] - this.dragSpecs.startPoint[1] :
37003                     this.dragSpecs.startPoint[1] - endPoint[1]
37004                 );
37005         }
37006         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37007         if(newSize != this.dragSpecs.startSize){
37008             if(this.fireEvent('beforeapply', this, newSize) !== false){
37009                 this.adapter.setElementSize(this, newSize);
37010                 this.fireEvent("moved", this, newSize);
37011                 this.fireEvent("resize", this, newSize);
37012             }
37013         }
37014     },
37015     
37016     /**
37017      * Get the adapter this SplitBar uses
37018      * @return The adapter object
37019      */
37020     getAdapter : function(){
37021         return this.adapter;
37022     },
37023     
37024     /**
37025      * Set the adapter this SplitBar uses
37026      * @param {Object} adapter A SplitBar adapter object
37027      */
37028     setAdapter : function(adapter){
37029         this.adapter = adapter;
37030         this.adapter.init(this);
37031     },
37032     
37033     /**
37034      * Gets the minimum size for the resizing element
37035      * @return {Number} The minimum size
37036      */
37037     getMinimumSize : function(){
37038         return this.minSize;
37039     },
37040     
37041     /**
37042      * Sets the minimum size for the resizing element
37043      * @param {Number} minSize The minimum size
37044      */
37045     setMinimumSize : function(minSize){
37046         this.minSize = minSize;
37047     },
37048     
37049     /**
37050      * Gets the maximum size for the resizing element
37051      * @return {Number} The maximum size
37052      */
37053     getMaximumSize : function(){
37054         return this.maxSize;
37055     },
37056     
37057     /**
37058      * Sets the maximum size for the resizing element
37059      * @param {Number} maxSize The maximum size
37060      */
37061     setMaximumSize : function(maxSize){
37062         this.maxSize = maxSize;
37063     },
37064     
37065     /**
37066      * Sets the initialize size for the resizing element
37067      * @param {Number} size The initial size
37068      */
37069     setCurrentSize : function(size){
37070         var oldAnimate = this.animate;
37071         this.animate = false;
37072         this.adapter.setElementSize(this, size);
37073         this.animate = oldAnimate;
37074     },
37075     
37076     /**
37077      * Destroy this splitbar. 
37078      * @param {Boolean} removeEl True to remove the element
37079      */
37080     destroy : function(removeEl){
37081         if(this.shim){
37082             this.shim.remove();
37083         }
37084         this.dd.unreg();
37085         this.proxy.parentNode.removeChild(this.proxy);
37086         if(removeEl){
37087             this.el.remove();
37088         }
37089     }
37090 });
37091
37092 /**
37093  * @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.
37094  */
37095 Roo.bootstrap.SplitBar.createProxy = function(dir){
37096     var proxy = new Roo.Element(document.createElement("div"));
37097     proxy.unselectable();
37098     var cls = 'roo-splitbar-proxy';
37099     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37100     document.body.appendChild(proxy.dom);
37101     return proxy.dom;
37102 };
37103
37104 /** 
37105  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37106  * Default Adapter. It assumes the splitter and resizing element are not positioned
37107  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37108  */
37109 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37110 };
37111
37112 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37113     // do nothing for now
37114     init : function(s){
37115     
37116     },
37117     /**
37118      * Called before drag operations to get the current size of the resizing element. 
37119      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37120      */
37121      getElementSize : function(s){
37122         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37123             return s.resizingEl.getWidth();
37124         }else{
37125             return s.resizingEl.getHeight();
37126         }
37127     },
37128     
37129     /**
37130      * Called after drag operations to set the size of the resizing element.
37131      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37132      * @param {Number} newSize The new size to set
37133      * @param {Function} onComplete A function to be invoked when resizing is complete
37134      */
37135     setElementSize : function(s, newSize, onComplete){
37136         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37137             if(!s.animate){
37138                 s.resizingEl.setWidth(newSize);
37139                 if(onComplete){
37140                     onComplete(s, newSize);
37141                 }
37142             }else{
37143                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37144             }
37145         }else{
37146             
37147             if(!s.animate){
37148                 s.resizingEl.setHeight(newSize);
37149                 if(onComplete){
37150                     onComplete(s, newSize);
37151                 }
37152             }else{
37153                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37154             }
37155         }
37156     }
37157 };
37158
37159 /** 
37160  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37161  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37162  * Adapter that  moves the splitter element to align with the resized sizing element. 
37163  * Used with an absolute positioned SplitBar.
37164  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37165  * document.body, make sure you assign an id to the body element.
37166  */
37167 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37168     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37169     this.container = Roo.get(container);
37170 };
37171
37172 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37173     init : function(s){
37174         this.basic.init(s);
37175     },
37176     
37177     getElementSize : function(s){
37178         return this.basic.getElementSize(s);
37179     },
37180     
37181     setElementSize : function(s, newSize, onComplete){
37182         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37183     },
37184     
37185     moveSplitter : function(s){
37186         var yes = Roo.bootstrap.SplitBar;
37187         switch(s.placement){
37188             case yes.LEFT:
37189                 s.el.setX(s.resizingEl.getRight());
37190                 break;
37191             case yes.RIGHT:
37192                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37193                 break;
37194             case yes.TOP:
37195                 s.el.setY(s.resizingEl.getBottom());
37196                 break;
37197             case yes.BOTTOM:
37198                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37199                 break;
37200         }
37201     }
37202 };
37203
37204 /**
37205  * Orientation constant - Create a vertical SplitBar
37206  * @static
37207  * @type Number
37208  */
37209 Roo.bootstrap.SplitBar.VERTICAL = 1;
37210
37211 /**
37212  * Orientation constant - Create a horizontal SplitBar
37213  * @static
37214  * @type Number
37215  */
37216 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37217
37218 /**
37219  * Placement constant - The resizing element is to the left of the splitter element
37220  * @static
37221  * @type Number
37222  */
37223 Roo.bootstrap.SplitBar.LEFT = 1;
37224
37225 /**
37226  * Placement constant - The resizing element is to the right of the splitter element
37227  * @static
37228  * @type Number
37229  */
37230 Roo.bootstrap.SplitBar.RIGHT = 2;
37231
37232 /**
37233  * Placement constant - The resizing element is positioned above the splitter element
37234  * @static
37235  * @type Number
37236  */
37237 Roo.bootstrap.SplitBar.TOP = 3;
37238
37239 /**
37240  * Placement constant - The resizing element is positioned under splitter element
37241  * @static
37242  * @type Number
37243  */
37244 Roo.bootstrap.SplitBar.BOTTOM = 4;
37245 Roo.namespace("Roo.bootstrap.layout");/*
37246  * Based on:
37247  * Ext JS Library 1.1.1
37248  * Copyright(c) 2006-2007, Ext JS, LLC.
37249  *
37250  * Originally Released Under LGPL - original licence link has changed is not relivant.
37251  *
37252  * Fork - LGPL
37253  * <script type="text/javascript">
37254  */
37255
37256 /**
37257  * @class Roo.bootstrap.layout.Manager
37258  * @extends Roo.bootstrap.Component
37259  * Base class for layout managers.
37260  */
37261 Roo.bootstrap.layout.Manager = function(config)
37262 {
37263     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37264
37265
37266
37267
37268
37269     /** false to disable window resize monitoring @type Boolean */
37270     this.monitorWindowResize = true;
37271     this.regions = {};
37272     this.addEvents({
37273         /**
37274          * @event layout
37275          * Fires when a layout is performed.
37276          * @param {Roo.LayoutManager} this
37277          */
37278         "layout" : true,
37279         /**
37280          * @event regionresized
37281          * Fires when the user resizes a region.
37282          * @param {Roo.LayoutRegion} region The resized region
37283          * @param {Number} newSize The new size (width for east/west, height for north/south)
37284          */
37285         "regionresized" : true,
37286         /**
37287          * @event regioncollapsed
37288          * Fires when a region is collapsed.
37289          * @param {Roo.LayoutRegion} region The collapsed region
37290          */
37291         "regioncollapsed" : true,
37292         /**
37293          * @event regionexpanded
37294          * Fires when a region is expanded.
37295          * @param {Roo.LayoutRegion} region The expanded region
37296          */
37297         "regionexpanded" : true
37298     });
37299     this.updating = false;
37300
37301     if (config.el) {
37302         this.el = Roo.get(config.el);
37303         this.initEvents();
37304     }
37305
37306 };
37307
37308 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37309
37310
37311     regions : null,
37312
37313     monitorWindowResize : true,
37314
37315
37316     updating : false,
37317
37318
37319     onRender : function(ct, position)
37320     {
37321         if(!this.el){
37322             this.el = Roo.get(ct);
37323             this.initEvents();
37324         }
37325         //this.fireEvent('render',this);
37326     },
37327
37328
37329     initEvents: function()
37330     {
37331
37332
37333         // ie scrollbar fix
37334         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37335             document.body.scroll = "no";
37336         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37337             this.el.position('relative');
37338         }
37339         this.id = this.el.id;
37340         this.el.addClass("roo-layout-container");
37341         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37342         if(this.el.dom != document.body ) {
37343             this.el.on('resize', this.layout,this);
37344             this.el.on('show', this.layout,this);
37345         }
37346
37347     },
37348
37349     /**
37350      * Returns true if this layout is currently being updated
37351      * @return {Boolean}
37352      */
37353     isUpdating : function(){
37354         return this.updating;
37355     },
37356
37357     /**
37358      * Suspend the LayoutManager from doing auto-layouts while
37359      * making multiple add or remove calls
37360      */
37361     beginUpdate : function(){
37362         this.updating = true;
37363     },
37364
37365     /**
37366      * Restore auto-layouts and optionally disable the manager from performing a layout
37367      * @param {Boolean} noLayout true to disable a layout update
37368      */
37369     endUpdate : function(noLayout){
37370         this.updating = false;
37371         if(!noLayout){
37372             this.layout();
37373         }
37374     },
37375
37376     layout: function(){
37377         // abstract...
37378     },
37379
37380     onRegionResized : function(region, newSize){
37381         this.fireEvent("regionresized", region, newSize);
37382         this.layout();
37383     },
37384
37385     onRegionCollapsed : function(region){
37386         this.fireEvent("regioncollapsed", region);
37387     },
37388
37389     onRegionExpanded : function(region){
37390         this.fireEvent("regionexpanded", region);
37391     },
37392
37393     /**
37394      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37395      * performs box-model adjustments.
37396      * @return {Object} The size as an object {width: (the width), height: (the height)}
37397      */
37398     getViewSize : function()
37399     {
37400         var size;
37401         if(this.el.dom != document.body){
37402             size = this.el.getSize();
37403         }else{
37404             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37405         }
37406         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37407         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37408         return size;
37409     },
37410
37411     /**
37412      * Returns the Element this layout is bound to.
37413      * @return {Roo.Element}
37414      */
37415     getEl : function(){
37416         return this.el;
37417     },
37418
37419     /**
37420      * Returns the specified region.
37421      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37422      * @return {Roo.LayoutRegion}
37423      */
37424     getRegion : function(target){
37425         return this.regions[target.toLowerCase()];
37426     },
37427
37428     onWindowResize : function(){
37429         if(this.monitorWindowResize){
37430             this.layout();
37431         }
37432     }
37433 });
37434 /*
37435  * Based on:
37436  * Ext JS Library 1.1.1
37437  * Copyright(c) 2006-2007, Ext JS, LLC.
37438  *
37439  * Originally Released Under LGPL - original licence link has changed is not relivant.
37440  *
37441  * Fork - LGPL
37442  * <script type="text/javascript">
37443  */
37444 /**
37445  * @class Roo.bootstrap.layout.Border
37446  * @extends Roo.bootstrap.layout.Manager
37447  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37448  * please see: examples/bootstrap/nested.html<br><br>
37449  
37450 <b>The container the layout is rendered into can be either the body element or any other element.
37451 If it is not the body element, the container needs to either be an absolute positioned element,
37452 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37453 the container size if it is not the body element.</b>
37454
37455 * @constructor
37456 * Create a new Border
37457 * @param {Object} config Configuration options
37458  */
37459 Roo.bootstrap.layout.Border = function(config){
37460     config = config || {};
37461     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37462     
37463     
37464     
37465     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37466         if(config[region]){
37467             config[region].region = region;
37468             this.addRegion(config[region]);
37469         }
37470     },this);
37471     
37472 };
37473
37474 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
37475
37476 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37477     
37478     parent : false, // this might point to a 'nest' or a ???
37479     
37480     /**
37481      * Creates and adds a new region if it doesn't already exist.
37482      * @param {String} target The target region key (north, south, east, west or center).
37483      * @param {Object} config The regions config object
37484      * @return {BorderLayoutRegion} The new region
37485      */
37486     addRegion : function(config)
37487     {
37488         if(!this.regions[config.region]){
37489             var r = this.factory(config);
37490             this.bindRegion(r);
37491         }
37492         return this.regions[config.region];
37493     },
37494
37495     // private (kinda)
37496     bindRegion : function(r){
37497         this.regions[r.config.region] = r;
37498         
37499         r.on("visibilitychange",    this.layout, this);
37500         r.on("paneladded",          this.layout, this);
37501         r.on("panelremoved",        this.layout, this);
37502         r.on("invalidated",         this.layout, this);
37503         r.on("resized",             this.onRegionResized, this);
37504         r.on("collapsed",           this.onRegionCollapsed, this);
37505         r.on("expanded",            this.onRegionExpanded, this);
37506     },
37507
37508     /**
37509      * Performs a layout update.
37510      */
37511     layout : function()
37512     {
37513         if(this.updating) {
37514             return;
37515         }
37516         
37517         // render all the rebions if they have not been done alreayd?
37518         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37519             if(this.regions[region] && !this.regions[region].bodyEl){
37520                 this.regions[region].onRender(this.el)
37521             }
37522         },this);
37523         
37524         var size = this.getViewSize();
37525         var w = size.width;
37526         var h = size.height;
37527         var centerW = w;
37528         var centerH = h;
37529         var centerY = 0;
37530         var centerX = 0;
37531         //var x = 0, y = 0;
37532
37533         var rs = this.regions;
37534         var north = rs["north"];
37535         var south = rs["south"]; 
37536         var west = rs["west"];
37537         var east = rs["east"];
37538         var center = rs["center"];
37539         //if(this.hideOnLayout){ // not supported anymore
37540             //c.el.setStyle("display", "none");
37541         //}
37542         if(north && north.isVisible()){
37543             var b = north.getBox();
37544             var m = north.getMargins();
37545             b.width = w - (m.left+m.right);
37546             b.x = m.left;
37547             b.y = m.top;
37548             centerY = b.height + b.y + m.bottom;
37549             centerH -= centerY;
37550             north.updateBox(this.safeBox(b));
37551         }
37552         if(south && south.isVisible()){
37553             var b = south.getBox();
37554             var m = south.getMargins();
37555             b.width = w - (m.left+m.right);
37556             b.x = m.left;
37557             var totalHeight = (b.height + m.top + m.bottom);
37558             b.y = h - totalHeight + m.top;
37559             centerH -= totalHeight;
37560             south.updateBox(this.safeBox(b));
37561         }
37562         if(west && west.isVisible()){
37563             var b = west.getBox();
37564             var m = west.getMargins();
37565             b.height = centerH - (m.top+m.bottom);
37566             b.x = m.left;
37567             b.y = centerY + m.top;
37568             var totalWidth = (b.width + m.left + m.right);
37569             centerX += totalWidth;
37570             centerW -= totalWidth;
37571             west.updateBox(this.safeBox(b));
37572         }
37573         if(east && east.isVisible()){
37574             var b = east.getBox();
37575             var m = east.getMargins();
37576             b.height = centerH - (m.top+m.bottom);
37577             var totalWidth = (b.width + m.left + m.right);
37578             b.x = w - totalWidth + m.left;
37579             b.y = centerY + m.top;
37580             centerW -= totalWidth;
37581             east.updateBox(this.safeBox(b));
37582         }
37583         if(center){
37584             var m = center.getMargins();
37585             var centerBox = {
37586                 x: centerX + m.left,
37587                 y: centerY + m.top,
37588                 width: centerW - (m.left+m.right),
37589                 height: centerH - (m.top+m.bottom)
37590             };
37591             //if(this.hideOnLayout){
37592                 //center.el.setStyle("display", "block");
37593             //}
37594             center.updateBox(this.safeBox(centerBox));
37595         }
37596         this.el.repaint();
37597         this.fireEvent("layout", this);
37598     },
37599
37600     // private
37601     safeBox : function(box){
37602         box.width = Math.max(0, box.width);
37603         box.height = Math.max(0, box.height);
37604         return box;
37605     },
37606
37607     /**
37608      * Adds a ContentPanel (or subclass) to this layout.
37609      * @param {String} target The target region key (north, south, east, west or center).
37610      * @param {Roo.ContentPanel} panel The panel to add
37611      * @return {Roo.ContentPanel} The added panel
37612      */
37613     add : function(target, panel){
37614          
37615         target = target.toLowerCase();
37616         return this.regions[target].add(panel);
37617     },
37618
37619     /**
37620      * Remove a ContentPanel (or subclass) to this layout.
37621      * @param {String} target The target region key (north, south, east, west or center).
37622      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37623      * @return {Roo.ContentPanel} The removed panel
37624      */
37625     remove : function(target, panel){
37626         target = target.toLowerCase();
37627         return this.regions[target].remove(panel);
37628     },
37629
37630     /**
37631      * Searches all regions for a panel with the specified id
37632      * @param {String} panelId
37633      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37634      */
37635     findPanel : function(panelId){
37636         var rs = this.regions;
37637         for(var target in rs){
37638             if(typeof rs[target] != "function"){
37639                 var p = rs[target].getPanel(panelId);
37640                 if(p){
37641                     return p;
37642                 }
37643             }
37644         }
37645         return null;
37646     },
37647
37648     /**
37649      * Searches all regions for a panel with the specified id and activates (shows) it.
37650      * @param {String/ContentPanel} panelId The panels id or the panel itself
37651      * @return {Roo.ContentPanel} The shown panel or null
37652      */
37653     showPanel : function(panelId) {
37654       var rs = this.regions;
37655       for(var target in rs){
37656          var r = rs[target];
37657          if(typeof r != "function"){
37658             if(r.hasPanel(panelId)){
37659                return r.showPanel(panelId);
37660             }
37661          }
37662       }
37663       return null;
37664    },
37665
37666    /**
37667      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37668      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37669      */
37670    /*
37671     restoreState : function(provider){
37672         if(!provider){
37673             provider = Roo.state.Manager;
37674         }
37675         var sm = new Roo.LayoutStateManager();
37676         sm.init(this, provider);
37677     },
37678 */
37679  
37680  
37681     /**
37682      * Adds a xtype elements to the layout.
37683      * <pre><code>
37684
37685 layout.addxtype({
37686        xtype : 'ContentPanel',
37687        region: 'west',
37688        items: [ .... ]
37689    }
37690 );
37691
37692 layout.addxtype({
37693         xtype : 'NestedLayoutPanel',
37694         region: 'west',
37695         layout: {
37696            center: { },
37697            west: { }   
37698         },
37699         items : [ ... list of content panels or nested layout panels.. ]
37700    }
37701 );
37702 </code></pre>
37703      * @param {Object} cfg Xtype definition of item to add.
37704      */
37705     addxtype : function(cfg)
37706     {
37707         // basically accepts a pannel...
37708         // can accept a layout region..!?!?
37709         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37710         
37711         
37712         // theory?  children can only be panels??
37713         
37714         //if (!cfg.xtype.match(/Panel$/)) {
37715         //    return false;
37716         //}
37717         var ret = false;
37718         
37719         if (typeof(cfg.region) == 'undefined') {
37720             Roo.log("Failed to add Panel, region was not set");
37721             Roo.log(cfg);
37722             return false;
37723         }
37724         var region = cfg.region;
37725         delete cfg.region;
37726         
37727           
37728         var xitems = [];
37729         if (cfg.items) {
37730             xitems = cfg.items;
37731             delete cfg.items;
37732         }
37733         var nb = false;
37734         
37735         if ( region == 'center') {
37736             Roo.log("Center: " + cfg.title);
37737         }
37738         
37739         
37740         switch(cfg.xtype) 
37741         {
37742             case 'Content':  // ContentPanel (el, cfg)
37743             case 'Scroll':  // ContentPanel (el, cfg)
37744             case 'View': 
37745                 cfg.autoCreate = cfg.autoCreate || true;
37746                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37747                 //} else {
37748                 //    var el = this.el.createChild();
37749                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37750                 //}
37751                 
37752                 this.add(region, ret);
37753                 break;
37754             
37755             /*
37756             case 'TreePanel': // our new panel!
37757                 cfg.el = this.el.createChild();
37758                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37759                 this.add(region, ret);
37760                 break;
37761             */
37762             
37763             case 'Nest': 
37764                 // create a new Layout (which is  a Border Layout...
37765                 
37766                 var clayout = cfg.layout;
37767                 clayout.el  = this.el.createChild();
37768                 clayout.items   = clayout.items  || [];
37769                 
37770                 delete cfg.layout;
37771                 
37772                 // replace this exitems with the clayout ones..
37773                 xitems = clayout.items;
37774                  
37775                 // force background off if it's in center...
37776                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37777                     cfg.background = false;
37778                 }
37779                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37780                 
37781                 
37782                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37783                 //console.log('adding nested layout panel '  + cfg.toSource());
37784                 this.add(region, ret);
37785                 nb = {}; /// find first...
37786                 break;
37787             
37788             case 'Grid':
37789                 
37790                 // needs grid and region
37791                 
37792                 //var el = this.getRegion(region).el.createChild();
37793                 /*
37794                  *var el = this.el.createChild();
37795                 // create the grid first...
37796                 cfg.grid.container = el;
37797                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37798                 */
37799                 
37800                 if (region == 'center' && this.active ) {
37801                     cfg.background = false;
37802                 }
37803                 
37804                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37805                 
37806                 this.add(region, ret);
37807                 /*
37808                 if (cfg.background) {
37809                     // render grid on panel activation (if panel background)
37810                     ret.on('activate', function(gp) {
37811                         if (!gp.grid.rendered) {
37812                     //        gp.grid.render(el);
37813                         }
37814                     });
37815                 } else {
37816                   //  cfg.grid.render(el);
37817                 }
37818                 */
37819                 break;
37820            
37821            
37822             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37823                 // it was the old xcomponent building that caused this before.
37824                 // espeically if border is the top element in the tree.
37825                 ret = this;
37826                 break; 
37827                 
37828                     
37829                 
37830                 
37831                 
37832             default:
37833                 /*
37834                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37835                     
37836                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37837                     this.add(region, ret);
37838                 } else {
37839                 */
37840                     Roo.log(cfg);
37841                     throw "Can not add '" + cfg.xtype + "' to Border";
37842                     return null;
37843              
37844                                 
37845              
37846         }
37847         this.beginUpdate();
37848         // add children..
37849         var region = '';
37850         var abn = {};
37851         Roo.each(xitems, function(i)  {
37852             region = nb && i.region ? i.region : false;
37853             
37854             var add = ret.addxtype(i);
37855            
37856             if (region) {
37857                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37858                 if (!i.background) {
37859                     abn[region] = nb[region] ;
37860                 }
37861             }
37862             
37863         });
37864         this.endUpdate();
37865
37866         // make the last non-background panel active..
37867         //if (nb) { Roo.log(abn); }
37868         if (nb) {
37869             
37870             for(var r in abn) {
37871                 region = this.getRegion(r);
37872                 if (region) {
37873                     // tried using nb[r], but it does not work..
37874                      
37875                     region.showPanel(abn[r]);
37876                    
37877                 }
37878             }
37879         }
37880         return ret;
37881         
37882     },
37883     
37884     
37885 // private
37886     factory : function(cfg)
37887     {
37888         
37889         var validRegions = Roo.bootstrap.layout.Border.regions;
37890
37891         var target = cfg.region;
37892         cfg.mgr = this;
37893         
37894         var r = Roo.bootstrap.layout;
37895         Roo.log(target);
37896         switch(target){
37897             case "north":
37898                 return new r.North(cfg);
37899             case "south":
37900                 return new r.South(cfg);
37901             case "east":
37902                 return new r.East(cfg);
37903             case "west":
37904                 return new r.West(cfg);
37905             case "center":
37906                 return new r.Center(cfg);
37907         }
37908         throw 'Layout region "'+target+'" not supported.';
37909     }
37910     
37911     
37912 });
37913  /*
37914  * Based on:
37915  * Ext JS Library 1.1.1
37916  * Copyright(c) 2006-2007, Ext JS, LLC.
37917  *
37918  * Originally Released Under LGPL - original licence link has changed is not relivant.
37919  *
37920  * Fork - LGPL
37921  * <script type="text/javascript">
37922  */
37923  
37924 /**
37925  * @class Roo.bootstrap.layout.Basic
37926  * @extends Roo.util.Observable
37927  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37928  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37929  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37930  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37931  * @cfg {string}   region  the region that it inhabits..
37932  * @cfg {bool}   skipConfig skip config?
37933  * 
37934
37935  */
37936 Roo.bootstrap.layout.Basic = function(config){
37937     
37938     this.mgr = config.mgr;
37939     
37940     this.position = config.region;
37941     
37942     var skipConfig = config.skipConfig;
37943     
37944     this.events = {
37945         /**
37946          * @scope Roo.BasicLayoutRegion
37947          */
37948         
37949         /**
37950          * @event beforeremove
37951          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37952          * @param {Roo.LayoutRegion} this
37953          * @param {Roo.ContentPanel} panel The panel
37954          * @param {Object} e The cancel event object
37955          */
37956         "beforeremove" : true,
37957         /**
37958          * @event invalidated
37959          * Fires when the layout for this region is changed.
37960          * @param {Roo.LayoutRegion} this
37961          */
37962         "invalidated" : true,
37963         /**
37964          * @event visibilitychange
37965          * Fires when this region is shown or hidden 
37966          * @param {Roo.LayoutRegion} this
37967          * @param {Boolean} visibility true or false
37968          */
37969         "visibilitychange" : true,
37970         /**
37971          * @event paneladded
37972          * Fires when a panel is added. 
37973          * @param {Roo.LayoutRegion} this
37974          * @param {Roo.ContentPanel} panel The panel
37975          */
37976         "paneladded" : true,
37977         /**
37978          * @event panelremoved
37979          * Fires when a panel is removed. 
37980          * @param {Roo.LayoutRegion} this
37981          * @param {Roo.ContentPanel} panel The panel
37982          */
37983         "panelremoved" : true,
37984         /**
37985          * @event beforecollapse
37986          * Fires when this region before collapse.
37987          * @param {Roo.LayoutRegion} this
37988          */
37989         "beforecollapse" : true,
37990         /**
37991          * @event collapsed
37992          * Fires when this region is collapsed.
37993          * @param {Roo.LayoutRegion} this
37994          */
37995         "collapsed" : true,
37996         /**
37997          * @event expanded
37998          * Fires when this region is expanded.
37999          * @param {Roo.LayoutRegion} this
38000          */
38001         "expanded" : true,
38002         /**
38003          * @event slideshow
38004          * Fires when this region is slid into view.
38005          * @param {Roo.LayoutRegion} this
38006          */
38007         "slideshow" : true,
38008         /**
38009          * @event slidehide
38010          * Fires when this region slides out of view. 
38011          * @param {Roo.LayoutRegion} this
38012          */
38013         "slidehide" : true,
38014         /**
38015          * @event panelactivated
38016          * Fires when a panel is activated. 
38017          * @param {Roo.LayoutRegion} this
38018          * @param {Roo.ContentPanel} panel The activated panel
38019          */
38020         "panelactivated" : true,
38021         /**
38022          * @event resized
38023          * Fires when the user resizes this region. 
38024          * @param {Roo.LayoutRegion} this
38025          * @param {Number} newSize The new size (width for east/west, height for north/south)
38026          */
38027         "resized" : true
38028     };
38029     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38030     this.panels = new Roo.util.MixedCollection();
38031     this.panels.getKey = this.getPanelId.createDelegate(this);
38032     this.box = null;
38033     this.activePanel = null;
38034     // ensure listeners are added...
38035     
38036     if (config.listeners || config.events) {
38037         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38038             listeners : config.listeners || {},
38039             events : config.events || {}
38040         });
38041     }
38042     
38043     if(skipConfig !== true){
38044         this.applyConfig(config);
38045     }
38046 };
38047
38048 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38049 {
38050     getPanelId : function(p){
38051         return p.getId();
38052     },
38053     
38054     applyConfig : function(config){
38055         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38056         this.config = config;
38057         
38058     },
38059     
38060     /**
38061      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38062      * the width, for horizontal (north, south) the height.
38063      * @param {Number} newSize The new width or height
38064      */
38065     resizeTo : function(newSize){
38066         var el = this.el ? this.el :
38067                  (this.activePanel ? this.activePanel.getEl() : null);
38068         if(el){
38069             switch(this.position){
38070                 case "east":
38071                 case "west":
38072                     el.setWidth(newSize);
38073                     this.fireEvent("resized", this, newSize);
38074                 break;
38075                 case "north":
38076                 case "south":
38077                     el.setHeight(newSize);
38078                     this.fireEvent("resized", this, newSize);
38079                 break;                
38080             }
38081         }
38082     },
38083     
38084     getBox : function(){
38085         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38086     },
38087     
38088     getMargins : function(){
38089         return this.margins;
38090     },
38091     
38092     updateBox : function(box){
38093         this.box = box;
38094         var el = this.activePanel.getEl();
38095         el.dom.style.left = box.x + "px";
38096         el.dom.style.top = box.y + "px";
38097         this.activePanel.setSize(box.width, box.height);
38098     },
38099     
38100     /**
38101      * Returns the container element for this region.
38102      * @return {Roo.Element}
38103      */
38104     getEl : function(){
38105         return this.activePanel;
38106     },
38107     
38108     /**
38109      * Returns true if this region is currently visible.
38110      * @return {Boolean}
38111      */
38112     isVisible : function(){
38113         return this.activePanel ? true : false;
38114     },
38115     
38116     setActivePanel : function(panel){
38117         panel = this.getPanel(panel);
38118         if(this.activePanel && this.activePanel != panel){
38119             this.activePanel.setActiveState(false);
38120             this.activePanel.getEl().setLeftTop(-10000,-10000);
38121         }
38122         this.activePanel = panel;
38123         panel.setActiveState(true);
38124         if(this.box){
38125             panel.setSize(this.box.width, this.box.height);
38126         }
38127         this.fireEvent("panelactivated", this, panel);
38128         this.fireEvent("invalidated");
38129     },
38130     
38131     /**
38132      * Show the specified panel.
38133      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38134      * @return {Roo.ContentPanel} The shown panel or null
38135      */
38136     showPanel : function(panel){
38137         panel = this.getPanel(panel);
38138         if(panel){
38139             this.setActivePanel(panel);
38140         }
38141         return panel;
38142     },
38143     
38144     /**
38145      * Get the active panel for this region.
38146      * @return {Roo.ContentPanel} The active panel or null
38147      */
38148     getActivePanel : function(){
38149         return this.activePanel;
38150     },
38151     
38152     /**
38153      * Add the passed ContentPanel(s)
38154      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38155      * @return {Roo.ContentPanel} The panel added (if only one was added)
38156      */
38157     add : function(panel){
38158         if(arguments.length > 1){
38159             for(var i = 0, len = arguments.length; i < len; i++) {
38160                 this.add(arguments[i]);
38161             }
38162             return null;
38163         }
38164         if(this.hasPanel(panel)){
38165             this.showPanel(panel);
38166             return panel;
38167         }
38168         var el = panel.getEl();
38169         if(el.dom.parentNode != this.mgr.el.dom){
38170             this.mgr.el.dom.appendChild(el.dom);
38171         }
38172         if(panel.setRegion){
38173             panel.setRegion(this);
38174         }
38175         this.panels.add(panel);
38176         el.setStyle("position", "absolute");
38177         if(!panel.background){
38178             this.setActivePanel(panel);
38179             if(this.config.initialSize && this.panels.getCount()==1){
38180                 this.resizeTo(this.config.initialSize);
38181             }
38182         }
38183         this.fireEvent("paneladded", this, panel);
38184         return panel;
38185     },
38186     
38187     /**
38188      * Returns true if the panel is in this region.
38189      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38190      * @return {Boolean}
38191      */
38192     hasPanel : function(panel){
38193         if(typeof panel == "object"){ // must be panel obj
38194             panel = panel.getId();
38195         }
38196         return this.getPanel(panel) ? true : false;
38197     },
38198     
38199     /**
38200      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38201      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38202      * @param {Boolean} preservePanel Overrides the config preservePanel option
38203      * @return {Roo.ContentPanel} The panel that was removed
38204      */
38205     remove : function(panel, preservePanel){
38206         panel = this.getPanel(panel);
38207         if(!panel){
38208             return null;
38209         }
38210         var e = {};
38211         this.fireEvent("beforeremove", this, panel, e);
38212         if(e.cancel === true){
38213             return null;
38214         }
38215         var panelId = panel.getId();
38216         this.panels.removeKey(panelId);
38217         return panel;
38218     },
38219     
38220     /**
38221      * Returns the panel specified or null if it's not in this region.
38222      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38223      * @return {Roo.ContentPanel}
38224      */
38225     getPanel : function(id){
38226         if(typeof id == "object"){ // must be panel obj
38227             return id;
38228         }
38229         return this.panels.get(id);
38230     },
38231     
38232     /**
38233      * Returns this regions position (north/south/east/west/center).
38234      * @return {String} 
38235      */
38236     getPosition: function(){
38237         return this.position;    
38238     }
38239 });/*
38240  * Based on:
38241  * Ext JS Library 1.1.1
38242  * Copyright(c) 2006-2007, Ext JS, LLC.
38243  *
38244  * Originally Released Under LGPL - original licence link has changed is not relivant.
38245  *
38246  * Fork - LGPL
38247  * <script type="text/javascript">
38248  */
38249  
38250 /**
38251  * @class Roo.bootstrap.layout.Region
38252  * @extends Roo.bootstrap.layout.Basic
38253  * This class represents a region in a layout manager.
38254  
38255  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38256  * @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})
38257  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38258  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38259  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38260  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38261  * @cfg {String}    title           The title for the region (overrides panel titles)
38262  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38263  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38264  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38265  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38266  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38267  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38268  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38269  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38270  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38271  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38272
38273  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38274  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38275  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38276  * @cfg {Number}    width           For East/West panels
38277  * @cfg {Number}    height          For North/South panels
38278  * @cfg {Boolean}   split           To show the splitter
38279  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38280  * 
38281  * @cfg {string}   cls             Extra CSS classes to add to region
38282  * 
38283  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38284  * @cfg {string}   region  the region that it inhabits..
38285  *
38286
38287  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38288  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38289
38290  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38291  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38292  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38293  */
38294 Roo.bootstrap.layout.Region = function(config)
38295 {
38296     this.applyConfig(config);
38297
38298     var mgr = config.mgr;
38299     var pos = config.region;
38300     config.skipConfig = true;
38301     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38302     
38303     if (mgr.el) {
38304         this.onRender(mgr.el);   
38305     }
38306      
38307     this.visible = true;
38308     this.collapsed = false;
38309     this.unrendered_panels = [];
38310 };
38311
38312 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38313
38314     position: '', // set by wrapper (eg. north/south etc..)
38315     unrendered_panels : null,  // unrendered panels.
38316     
38317     tabPosition : false,
38318     
38319     mgr: false, // points to 'Border'
38320     
38321     
38322     createBody : function(){
38323         /** This region's body element 
38324         * @type Roo.Element */
38325         this.bodyEl = this.el.createChild({
38326                 tag: "div",
38327                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38328         });
38329     },
38330
38331     onRender: function(ctr, pos)
38332     {
38333         var dh = Roo.DomHelper;
38334         /** This region's container element 
38335         * @type Roo.Element */
38336         this.el = dh.append(ctr.dom, {
38337                 tag: "div",
38338                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38339             }, true);
38340         /** This region's title element 
38341         * @type Roo.Element */
38342     
38343         this.titleEl = dh.append(this.el.dom,  {
38344                 tag: "div",
38345                 unselectable: "on",
38346                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38347                 children:[
38348                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38349                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38350                 ]
38351             }, true);
38352         
38353         this.titleEl.enableDisplayMode();
38354         /** This region's title text element 
38355         * @type HTMLElement */
38356         this.titleTextEl = this.titleEl.dom.firstChild;
38357         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38358         /*
38359         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38360         this.closeBtn.enableDisplayMode();
38361         this.closeBtn.on("click", this.closeClicked, this);
38362         this.closeBtn.hide();
38363     */
38364         this.createBody(this.config);
38365         if(this.config.hideWhenEmpty){
38366             this.hide();
38367             this.on("paneladded", this.validateVisibility, this);
38368             this.on("panelremoved", this.validateVisibility, this);
38369         }
38370         if(this.autoScroll){
38371             this.bodyEl.setStyle("overflow", "auto");
38372         }else{
38373             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38374         }
38375         //if(c.titlebar !== false){
38376             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38377                 this.titleEl.hide();
38378             }else{
38379                 this.titleEl.show();
38380                 if(this.config.title){
38381                     this.titleTextEl.innerHTML = this.config.title;
38382                 }
38383             }
38384         //}
38385         if(this.config.collapsed){
38386             this.collapse(true);
38387         }
38388         if(this.config.hidden){
38389             this.hide();
38390         }
38391         
38392         if (this.unrendered_panels && this.unrendered_panels.length) {
38393             for (var i =0;i< this.unrendered_panels.length; i++) {
38394                 this.add(this.unrendered_panels[i]);
38395             }
38396             this.unrendered_panels = null;
38397             
38398         }
38399         
38400     },
38401     
38402     applyConfig : function(c)
38403     {
38404         /*
38405          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38406             var dh = Roo.DomHelper;
38407             if(c.titlebar !== false){
38408                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38409                 this.collapseBtn.on("click", this.collapse, this);
38410                 this.collapseBtn.enableDisplayMode();
38411                 /*
38412                 if(c.showPin === true || this.showPin){
38413                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38414                     this.stickBtn.enableDisplayMode();
38415                     this.stickBtn.on("click", this.expand, this);
38416                     this.stickBtn.hide();
38417                 }
38418                 
38419             }
38420             */
38421             /** This region's collapsed element
38422             * @type Roo.Element */
38423             /*
38424              *
38425             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38426                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38427             ]}, true);
38428             
38429             if(c.floatable !== false){
38430                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38431                this.collapsedEl.on("click", this.collapseClick, this);
38432             }
38433
38434             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38435                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38436                    id: "message", unselectable: "on", style:{"float":"left"}});
38437                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38438              }
38439             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38440             this.expandBtn.on("click", this.expand, this);
38441             
38442         }
38443         
38444         if(this.collapseBtn){
38445             this.collapseBtn.setVisible(c.collapsible == true);
38446         }
38447         
38448         this.cmargins = c.cmargins || this.cmargins ||
38449                          (this.position == "west" || this.position == "east" ?
38450                              {top: 0, left: 2, right:2, bottom: 0} :
38451                              {top: 2, left: 0, right:0, bottom: 2});
38452         */
38453         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38454         
38455         
38456         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38457         
38458         this.autoScroll = c.autoScroll || false;
38459         
38460         
38461        
38462         
38463         this.duration = c.duration || .30;
38464         this.slideDuration = c.slideDuration || .45;
38465         this.config = c;
38466        
38467     },
38468     /**
38469      * Returns true if this region is currently visible.
38470      * @return {Boolean}
38471      */
38472     isVisible : function(){
38473         return this.visible;
38474     },
38475
38476     /**
38477      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38478      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38479      */
38480     //setCollapsedTitle : function(title){
38481     //    title = title || "&#160;";
38482      //   if(this.collapsedTitleTextEl){
38483       //      this.collapsedTitleTextEl.innerHTML = title;
38484        // }
38485     //},
38486
38487     getBox : function(){
38488         var b;
38489       //  if(!this.collapsed){
38490             b = this.el.getBox(false, true);
38491        // }else{
38492           //  b = this.collapsedEl.getBox(false, true);
38493         //}
38494         return b;
38495     },
38496
38497     getMargins : function(){
38498         return this.margins;
38499         //return this.collapsed ? this.cmargins : this.margins;
38500     },
38501 /*
38502     highlight : function(){
38503         this.el.addClass("x-layout-panel-dragover");
38504     },
38505
38506     unhighlight : function(){
38507         this.el.removeClass("x-layout-panel-dragover");
38508     },
38509 */
38510     updateBox : function(box)
38511     {
38512         if (!this.bodyEl) {
38513             return; // not rendered yet..
38514         }
38515         
38516         this.box = box;
38517         if(!this.collapsed){
38518             this.el.dom.style.left = box.x + "px";
38519             this.el.dom.style.top = box.y + "px";
38520             this.updateBody(box.width, box.height);
38521         }else{
38522             this.collapsedEl.dom.style.left = box.x + "px";
38523             this.collapsedEl.dom.style.top = box.y + "px";
38524             this.collapsedEl.setSize(box.width, box.height);
38525         }
38526         if(this.tabs){
38527             this.tabs.autoSizeTabs();
38528         }
38529     },
38530
38531     updateBody : function(w, h)
38532     {
38533         if(w !== null){
38534             this.el.setWidth(w);
38535             w -= this.el.getBorderWidth("rl");
38536             if(this.config.adjustments){
38537                 w += this.config.adjustments[0];
38538             }
38539         }
38540         if(h !== null && h > 0){
38541             this.el.setHeight(h);
38542             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38543             h -= this.el.getBorderWidth("tb");
38544             if(this.config.adjustments){
38545                 h += this.config.adjustments[1];
38546             }
38547             this.bodyEl.setHeight(h);
38548             if(this.tabs){
38549                 h = this.tabs.syncHeight(h);
38550             }
38551         }
38552         if(this.panelSize){
38553             w = w !== null ? w : this.panelSize.width;
38554             h = h !== null ? h : this.panelSize.height;
38555         }
38556         if(this.activePanel){
38557             var el = this.activePanel.getEl();
38558             w = w !== null ? w : el.getWidth();
38559             h = h !== null ? h : el.getHeight();
38560             this.panelSize = {width: w, height: h};
38561             this.activePanel.setSize(w, h);
38562         }
38563         if(Roo.isIE && this.tabs){
38564             this.tabs.el.repaint();
38565         }
38566     },
38567
38568     /**
38569      * Returns the container element for this region.
38570      * @return {Roo.Element}
38571      */
38572     getEl : function(){
38573         return this.el;
38574     },
38575
38576     /**
38577      * Hides this region.
38578      */
38579     hide : function(){
38580         //if(!this.collapsed){
38581             this.el.dom.style.left = "-2000px";
38582             this.el.hide();
38583         //}else{
38584          //   this.collapsedEl.dom.style.left = "-2000px";
38585          //   this.collapsedEl.hide();
38586        // }
38587         this.visible = false;
38588         this.fireEvent("visibilitychange", this, false);
38589     },
38590
38591     /**
38592      * Shows this region if it was previously hidden.
38593      */
38594     show : function(){
38595         //if(!this.collapsed){
38596             this.el.show();
38597         //}else{
38598         //    this.collapsedEl.show();
38599        // }
38600         this.visible = true;
38601         this.fireEvent("visibilitychange", this, true);
38602     },
38603 /*
38604     closeClicked : function(){
38605         if(this.activePanel){
38606             this.remove(this.activePanel);
38607         }
38608     },
38609
38610     collapseClick : function(e){
38611         if(this.isSlid){
38612            e.stopPropagation();
38613            this.slideIn();
38614         }else{
38615            e.stopPropagation();
38616            this.slideOut();
38617         }
38618     },
38619 */
38620     /**
38621      * Collapses this region.
38622      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38623      */
38624     /*
38625     collapse : function(skipAnim, skipCheck = false){
38626         if(this.collapsed) {
38627             return;
38628         }
38629         
38630         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38631             
38632             this.collapsed = true;
38633             if(this.split){
38634                 this.split.el.hide();
38635             }
38636             if(this.config.animate && skipAnim !== true){
38637                 this.fireEvent("invalidated", this);
38638                 this.animateCollapse();
38639             }else{
38640                 this.el.setLocation(-20000,-20000);
38641                 this.el.hide();
38642                 this.collapsedEl.show();
38643                 this.fireEvent("collapsed", this);
38644                 this.fireEvent("invalidated", this);
38645             }
38646         }
38647         
38648     },
38649 */
38650     animateCollapse : function(){
38651         // overridden
38652     },
38653
38654     /**
38655      * Expands this region if it was previously collapsed.
38656      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38657      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38658      */
38659     /*
38660     expand : function(e, skipAnim){
38661         if(e) {
38662             e.stopPropagation();
38663         }
38664         if(!this.collapsed || this.el.hasActiveFx()) {
38665             return;
38666         }
38667         if(this.isSlid){
38668             this.afterSlideIn();
38669             skipAnim = true;
38670         }
38671         this.collapsed = false;
38672         if(this.config.animate && skipAnim !== true){
38673             this.animateExpand();
38674         }else{
38675             this.el.show();
38676             if(this.split){
38677                 this.split.el.show();
38678             }
38679             this.collapsedEl.setLocation(-2000,-2000);
38680             this.collapsedEl.hide();
38681             this.fireEvent("invalidated", this);
38682             this.fireEvent("expanded", this);
38683         }
38684     },
38685 */
38686     animateExpand : function(){
38687         // overridden
38688     },
38689
38690     initTabs : function()
38691     {
38692         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38693         
38694         var ts = new Roo.bootstrap.panel.Tabs({
38695             el: this.bodyEl.dom,
38696             region : this,
38697             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38698             disableTooltips: this.config.disableTabTips,
38699             toolbar : this.config.toolbar
38700         });
38701         
38702         if(this.config.hideTabs){
38703             ts.stripWrap.setDisplayed(false);
38704         }
38705         this.tabs = ts;
38706         ts.resizeTabs = this.config.resizeTabs === true;
38707         ts.minTabWidth = this.config.minTabWidth || 40;
38708         ts.maxTabWidth = this.config.maxTabWidth || 250;
38709         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38710         ts.monitorResize = false;
38711         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38712         ts.bodyEl.addClass('roo-layout-tabs-body');
38713         this.panels.each(this.initPanelAsTab, this);
38714     },
38715
38716     initPanelAsTab : function(panel){
38717         var ti = this.tabs.addTab(
38718             panel.getEl().id,
38719             panel.getTitle(),
38720             null,
38721             this.config.closeOnTab && panel.isClosable(),
38722             panel.tpl
38723         );
38724         if(panel.tabTip !== undefined){
38725             ti.setTooltip(panel.tabTip);
38726         }
38727         ti.on("activate", function(){
38728               this.setActivePanel(panel);
38729         }, this);
38730         
38731         if(this.config.closeOnTab){
38732             ti.on("beforeclose", function(t, e){
38733                 e.cancel = true;
38734                 this.remove(panel);
38735             }, this);
38736         }
38737         
38738         panel.tabItem = ti;
38739         
38740         return ti;
38741     },
38742
38743     updatePanelTitle : function(panel, title)
38744     {
38745         if(this.activePanel == panel){
38746             this.updateTitle(title);
38747         }
38748         if(this.tabs){
38749             var ti = this.tabs.getTab(panel.getEl().id);
38750             ti.setText(title);
38751             if(panel.tabTip !== undefined){
38752                 ti.setTooltip(panel.tabTip);
38753             }
38754         }
38755     },
38756
38757     updateTitle : function(title){
38758         if(this.titleTextEl && !this.config.title){
38759             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38760         }
38761     },
38762
38763     setActivePanel : function(panel)
38764     {
38765         panel = this.getPanel(panel);
38766         if(this.activePanel && this.activePanel != panel){
38767             if(this.activePanel.setActiveState(false) === false){
38768                 return;
38769             }
38770         }
38771         this.activePanel = panel;
38772         panel.setActiveState(true);
38773         if(this.panelSize){
38774             panel.setSize(this.panelSize.width, this.panelSize.height);
38775         }
38776         if(this.closeBtn){
38777             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38778         }
38779         this.updateTitle(panel.getTitle());
38780         if(this.tabs){
38781             this.fireEvent("invalidated", this);
38782         }
38783         this.fireEvent("panelactivated", this, panel);
38784     },
38785
38786     /**
38787      * Shows the specified panel.
38788      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38789      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38790      */
38791     showPanel : function(panel)
38792     {
38793         panel = this.getPanel(panel);
38794         if(panel){
38795             if(this.tabs){
38796                 var tab = this.tabs.getTab(panel.getEl().id);
38797                 if(tab.isHidden()){
38798                     this.tabs.unhideTab(tab.id);
38799                 }
38800                 tab.activate();
38801             }else{
38802                 this.setActivePanel(panel);
38803             }
38804         }
38805         return panel;
38806     },
38807
38808     /**
38809      * Get the active panel for this region.
38810      * @return {Roo.ContentPanel} The active panel or null
38811      */
38812     getActivePanel : function(){
38813         return this.activePanel;
38814     },
38815
38816     validateVisibility : function(){
38817         if(this.panels.getCount() < 1){
38818             this.updateTitle("&#160;");
38819             this.closeBtn.hide();
38820             this.hide();
38821         }else{
38822             if(!this.isVisible()){
38823                 this.show();
38824             }
38825         }
38826     },
38827
38828     /**
38829      * Adds the passed ContentPanel(s) to this region.
38830      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38831      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38832      */
38833     add : function(panel)
38834     {
38835         if(arguments.length > 1){
38836             for(var i = 0, len = arguments.length; i < len; i++) {
38837                 this.add(arguments[i]);
38838             }
38839             return null;
38840         }
38841         
38842         // if we have not been rendered yet, then we can not really do much of this..
38843         if (!this.bodyEl) {
38844             this.unrendered_panels.push(panel);
38845             return panel;
38846         }
38847         
38848         
38849         
38850         
38851         if(this.hasPanel(panel)){
38852             this.showPanel(panel);
38853             return panel;
38854         }
38855         panel.setRegion(this);
38856         this.panels.add(panel);
38857        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38858             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38859             // and hide them... ???
38860             this.bodyEl.dom.appendChild(panel.getEl().dom);
38861             if(panel.background !== true){
38862                 this.setActivePanel(panel);
38863             }
38864             this.fireEvent("paneladded", this, panel);
38865             return panel;
38866         }
38867         */
38868         if(!this.tabs){
38869             this.initTabs();
38870         }else{
38871             this.initPanelAsTab(panel);
38872         }
38873         
38874         
38875         if(panel.background !== true){
38876             this.tabs.activate(panel.getEl().id);
38877         }
38878         this.fireEvent("paneladded", this, panel);
38879         return panel;
38880     },
38881
38882     /**
38883      * Hides the tab for the specified panel.
38884      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38885      */
38886     hidePanel : function(panel){
38887         if(this.tabs && (panel = this.getPanel(panel))){
38888             this.tabs.hideTab(panel.getEl().id);
38889         }
38890     },
38891
38892     /**
38893      * Unhides the tab for a previously hidden panel.
38894      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38895      */
38896     unhidePanel : function(panel){
38897         if(this.tabs && (panel = this.getPanel(panel))){
38898             this.tabs.unhideTab(panel.getEl().id);
38899         }
38900     },
38901
38902     clearPanels : function(){
38903         while(this.panels.getCount() > 0){
38904              this.remove(this.panels.first());
38905         }
38906     },
38907
38908     /**
38909      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38910      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38911      * @param {Boolean} preservePanel Overrides the config preservePanel option
38912      * @return {Roo.ContentPanel} The panel that was removed
38913      */
38914     remove : function(panel, preservePanel)
38915     {
38916         panel = this.getPanel(panel);
38917         if(!panel){
38918             return null;
38919         }
38920         var e = {};
38921         this.fireEvent("beforeremove", this, panel, e);
38922         if(e.cancel === true){
38923             return null;
38924         }
38925         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38926         var panelId = panel.getId();
38927         this.panels.removeKey(panelId);
38928         if(preservePanel){
38929             document.body.appendChild(panel.getEl().dom);
38930         }
38931         if(this.tabs){
38932             this.tabs.removeTab(panel.getEl().id);
38933         }else if (!preservePanel){
38934             this.bodyEl.dom.removeChild(panel.getEl().dom);
38935         }
38936         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38937             var p = this.panels.first();
38938             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38939             tempEl.appendChild(p.getEl().dom);
38940             this.bodyEl.update("");
38941             this.bodyEl.dom.appendChild(p.getEl().dom);
38942             tempEl = null;
38943             this.updateTitle(p.getTitle());
38944             this.tabs = null;
38945             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38946             this.setActivePanel(p);
38947         }
38948         panel.setRegion(null);
38949         if(this.activePanel == panel){
38950             this.activePanel = null;
38951         }
38952         if(this.config.autoDestroy !== false && preservePanel !== true){
38953             try{panel.destroy();}catch(e){}
38954         }
38955         this.fireEvent("panelremoved", this, panel);
38956         return panel;
38957     },
38958
38959     /**
38960      * Returns the TabPanel component used by this region
38961      * @return {Roo.TabPanel}
38962      */
38963     getTabs : function(){
38964         return this.tabs;
38965     },
38966
38967     createTool : function(parentEl, className){
38968         var btn = Roo.DomHelper.append(parentEl, {
38969             tag: "div",
38970             cls: "x-layout-tools-button",
38971             children: [ {
38972                 tag: "div",
38973                 cls: "roo-layout-tools-button-inner " + className,
38974                 html: "&#160;"
38975             }]
38976         }, true);
38977         btn.addClassOnOver("roo-layout-tools-button-over");
38978         return btn;
38979     }
38980 });/*
38981  * Based on:
38982  * Ext JS Library 1.1.1
38983  * Copyright(c) 2006-2007, Ext JS, LLC.
38984  *
38985  * Originally Released Under LGPL - original licence link has changed is not relivant.
38986  *
38987  * Fork - LGPL
38988  * <script type="text/javascript">
38989  */
38990  
38991
38992
38993 /**
38994  * @class Roo.SplitLayoutRegion
38995  * @extends Roo.LayoutRegion
38996  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38997  */
38998 Roo.bootstrap.layout.Split = function(config){
38999     this.cursor = config.cursor;
39000     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39001 };
39002
39003 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39004 {
39005     splitTip : "Drag to resize.",
39006     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39007     useSplitTips : false,
39008
39009     applyConfig : function(config){
39010         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39011     },
39012     
39013     onRender : function(ctr,pos) {
39014         
39015         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39016         if(!this.config.split){
39017             return;
39018         }
39019         if(!this.split){
39020             
39021             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39022                             tag: "div",
39023                             id: this.el.id + "-split",
39024                             cls: "roo-layout-split roo-layout-split-"+this.position,
39025                             html: "&#160;"
39026             });
39027             /** The SplitBar for this region 
39028             * @type Roo.SplitBar */
39029             // does not exist yet...
39030             Roo.log([this.position, this.orientation]);
39031             
39032             this.split = new Roo.bootstrap.SplitBar({
39033                 dragElement : splitEl,
39034                 resizingElement: this.el,
39035                 orientation : this.orientation
39036             });
39037             
39038             this.split.on("moved", this.onSplitMove, this);
39039             this.split.useShim = this.config.useShim === true;
39040             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39041             if(this.useSplitTips){
39042                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39043             }
39044             //if(config.collapsible){
39045             //    this.split.el.on("dblclick", this.collapse,  this);
39046             //}
39047         }
39048         if(typeof this.config.minSize != "undefined"){
39049             this.split.minSize = this.config.minSize;
39050         }
39051         if(typeof this.config.maxSize != "undefined"){
39052             this.split.maxSize = this.config.maxSize;
39053         }
39054         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39055             this.hideSplitter();
39056         }
39057         
39058     },
39059
39060     getHMaxSize : function(){
39061          var cmax = this.config.maxSize || 10000;
39062          var center = this.mgr.getRegion("center");
39063          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39064     },
39065
39066     getVMaxSize : function(){
39067          var cmax = this.config.maxSize || 10000;
39068          var center = this.mgr.getRegion("center");
39069          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39070     },
39071
39072     onSplitMove : function(split, newSize){
39073         this.fireEvent("resized", this, newSize);
39074     },
39075     
39076     /** 
39077      * Returns the {@link Roo.SplitBar} for this region.
39078      * @return {Roo.SplitBar}
39079      */
39080     getSplitBar : function(){
39081         return this.split;
39082     },
39083     
39084     hide : function(){
39085         this.hideSplitter();
39086         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39087     },
39088
39089     hideSplitter : function(){
39090         if(this.split){
39091             this.split.el.setLocation(-2000,-2000);
39092             this.split.el.hide();
39093         }
39094     },
39095
39096     show : function(){
39097         if(this.split){
39098             this.split.el.show();
39099         }
39100         Roo.bootstrap.layout.Split.superclass.show.call(this);
39101     },
39102     
39103     beforeSlide: function(){
39104         if(Roo.isGecko){// firefox overflow auto bug workaround
39105             this.bodyEl.clip();
39106             if(this.tabs) {
39107                 this.tabs.bodyEl.clip();
39108             }
39109             if(this.activePanel){
39110                 this.activePanel.getEl().clip();
39111                 
39112                 if(this.activePanel.beforeSlide){
39113                     this.activePanel.beforeSlide();
39114                 }
39115             }
39116         }
39117     },
39118     
39119     afterSlide : function(){
39120         if(Roo.isGecko){// firefox overflow auto bug workaround
39121             this.bodyEl.unclip();
39122             if(this.tabs) {
39123                 this.tabs.bodyEl.unclip();
39124             }
39125             if(this.activePanel){
39126                 this.activePanel.getEl().unclip();
39127                 if(this.activePanel.afterSlide){
39128                     this.activePanel.afterSlide();
39129                 }
39130             }
39131         }
39132     },
39133
39134     initAutoHide : function(){
39135         if(this.autoHide !== false){
39136             if(!this.autoHideHd){
39137                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39138                 this.autoHideHd = {
39139                     "mouseout": function(e){
39140                         if(!e.within(this.el, true)){
39141                             st.delay(500);
39142                         }
39143                     },
39144                     "mouseover" : function(e){
39145                         st.cancel();
39146                     },
39147                     scope : this
39148                 };
39149             }
39150             this.el.on(this.autoHideHd);
39151         }
39152     },
39153
39154     clearAutoHide : function(){
39155         if(this.autoHide !== false){
39156             this.el.un("mouseout", this.autoHideHd.mouseout);
39157             this.el.un("mouseover", this.autoHideHd.mouseover);
39158         }
39159     },
39160
39161     clearMonitor : function(){
39162         Roo.get(document).un("click", this.slideInIf, this);
39163     },
39164
39165     // these names are backwards but not changed for compat
39166     slideOut : function(){
39167         if(this.isSlid || this.el.hasActiveFx()){
39168             return;
39169         }
39170         this.isSlid = true;
39171         if(this.collapseBtn){
39172             this.collapseBtn.hide();
39173         }
39174         this.closeBtnState = this.closeBtn.getStyle('display');
39175         this.closeBtn.hide();
39176         if(this.stickBtn){
39177             this.stickBtn.show();
39178         }
39179         this.el.show();
39180         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39181         this.beforeSlide();
39182         this.el.setStyle("z-index", 10001);
39183         this.el.slideIn(this.getSlideAnchor(), {
39184             callback: function(){
39185                 this.afterSlide();
39186                 this.initAutoHide();
39187                 Roo.get(document).on("click", this.slideInIf, this);
39188                 this.fireEvent("slideshow", this);
39189             },
39190             scope: this,
39191             block: true
39192         });
39193     },
39194
39195     afterSlideIn : function(){
39196         this.clearAutoHide();
39197         this.isSlid = false;
39198         this.clearMonitor();
39199         this.el.setStyle("z-index", "");
39200         if(this.collapseBtn){
39201             this.collapseBtn.show();
39202         }
39203         this.closeBtn.setStyle('display', this.closeBtnState);
39204         if(this.stickBtn){
39205             this.stickBtn.hide();
39206         }
39207         this.fireEvent("slidehide", this);
39208     },
39209
39210     slideIn : function(cb){
39211         if(!this.isSlid || this.el.hasActiveFx()){
39212             Roo.callback(cb);
39213             return;
39214         }
39215         this.isSlid = false;
39216         this.beforeSlide();
39217         this.el.slideOut(this.getSlideAnchor(), {
39218             callback: function(){
39219                 this.el.setLeftTop(-10000, -10000);
39220                 this.afterSlide();
39221                 this.afterSlideIn();
39222                 Roo.callback(cb);
39223             },
39224             scope: this,
39225             block: true
39226         });
39227     },
39228     
39229     slideInIf : function(e){
39230         if(!e.within(this.el)){
39231             this.slideIn();
39232         }
39233     },
39234
39235     animateCollapse : function(){
39236         this.beforeSlide();
39237         this.el.setStyle("z-index", 20000);
39238         var anchor = this.getSlideAnchor();
39239         this.el.slideOut(anchor, {
39240             callback : function(){
39241                 this.el.setStyle("z-index", "");
39242                 this.collapsedEl.slideIn(anchor, {duration:.3});
39243                 this.afterSlide();
39244                 this.el.setLocation(-10000,-10000);
39245                 this.el.hide();
39246                 this.fireEvent("collapsed", this);
39247             },
39248             scope: this,
39249             block: true
39250         });
39251     },
39252
39253     animateExpand : function(){
39254         this.beforeSlide();
39255         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39256         this.el.setStyle("z-index", 20000);
39257         this.collapsedEl.hide({
39258             duration:.1
39259         });
39260         this.el.slideIn(this.getSlideAnchor(), {
39261             callback : function(){
39262                 this.el.setStyle("z-index", "");
39263                 this.afterSlide();
39264                 if(this.split){
39265                     this.split.el.show();
39266                 }
39267                 this.fireEvent("invalidated", this);
39268                 this.fireEvent("expanded", this);
39269             },
39270             scope: this,
39271             block: true
39272         });
39273     },
39274
39275     anchors : {
39276         "west" : "left",
39277         "east" : "right",
39278         "north" : "top",
39279         "south" : "bottom"
39280     },
39281
39282     sanchors : {
39283         "west" : "l",
39284         "east" : "r",
39285         "north" : "t",
39286         "south" : "b"
39287     },
39288
39289     canchors : {
39290         "west" : "tl-tr",
39291         "east" : "tr-tl",
39292         "north" : "tl-bl",
39293         "south" : "bl-tl"
39294     },
39295
39296     getAnchor : function(){
39297         return this.anchors[this.position];
39298     },
39299
39300     getCollapseAnchor : function(){
39301         return this.canchors[this.position];
39302     },
39303
39304     getSlideAnchor : function(){
39305         return this.sanchors[this.position];
39306     },
39307
39308     getAlignAdj : function(){
39309         var cm = this.cmargins;
39310         switch(this.position){
39311             case "west":
39312                 return [0, 0];
39313             break;
39314             case "east":
39315                 return [0, 0];
39316             break;
39317             case "north":
39318                 return [0, 0];
39319             break;
39320             case "south":
39321                 return [0, 0];
39322             break;
39323         }
39324     },
39325
39326     getExpandAdj : function(){
39327         var c = this.collapsedEl, cm = this.cmargins;
39328         switch(this.position){
39329             case "west":
39330                 return [-(cm.right+c.getWidth()+cm.left), 0];
39331             break;
39332             case "east":
39333                 return [cm.right+c.getWidth()+cm.left, 0];
39334             break;
39335             case "north":
39336                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39337             break;
39338             case "south":
39339                 return [0, cm.top+cm.bottom+c.getHeight()];
39340             break;
39341         }
39342     }
39343 });/*
39344  * Based on:
39345  * Ext JS Library 1.1.1
39346  * Copyright(c) 2006-2007, Ext JS, LLC.
39347  *
39348  * Originally Released Under LGPL - original licence link has changed is not relivant.
39349  *
39350  * Fork - LGPL
39351  * <script type="text/javascript">
39352  */
39353 /*
39354  * These classes are private internal classes
39355  */
39356 Roo.bootstrap.layout.Center = function(config){
39357     config.region = "center";
39358     Roo.bootstrap.layout.Region.call(this, config);
39359     this.visible = true;
39360     this.minWidth = config.minWidth || 20;
39361     this.minHeight = config.minHeight || 20;
39362 };
39363
39364 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39365     hide : function(){
39366         // center panel can't be hidden
39367     },
39368     
39369     show : function(){
39370         // center panel can't be hidden
39371     },
39372     
39373     getMinWidth: function(){
39374         return this.minWidth;
39375     },
39376     
39377     getMinHeight: function(){
39378         return this.minHeight;
39379     }
39380 });
39381
39382
39383
39384
39385  
39386
39387
39388
39389
39390
39391
39392 Roo.bootstrap.layout.North = function(config)
39393 {
39394     config.region = 'north';
39395     config.cursor = 'n-resize';
39396     
39397     Roo.bootstrap.layout.Split.call(this, config);
39398     
39399     
39400     if(this.split){
39401         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39402         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39403         this.split.el.addClass("roo-layout-split-v");
39404     }
39405     //var size = config.initialSize || config.height;
39406     //if(this.el && typeof size != "undefined"){
39407     //    this.el.setHeight(size);
39408     //}
39409 };
39410 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39411 {
39412     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39413      
39414      
39415     onRender : function(ctr, pos)
39416     {
39417         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39418         var size = this.config.initialSize || this.config.height;
39419         if(this.el && typeof size != "undefined"){
39420             this.el.setHeight(size);
39421         }
39422     
39423     },
39424     
39425     getBox : function(){
39426         if(this.collapsed){
39427             return this.collapsedEl.getBox();
39428         }
39429         var box = this.el.getBox();
39430         if(this.split){
39431             box.height += this.split.el.getHeight();
39432         }
39433         return box;
39434     },
39435     
39436     updateBox : function(box){
39437         if(this.split && !this.collapsed){
39438             box.height -= this.split.el.getHeight();
39439             this.split.el.setLeft(box.x);
39440             this.split.el.setTop(box.y+box.height);
39441             this.split.el.setWidth(box.width);
39442         }
39443         if(this.collapsed){
39444             this.updateBody(box.width, null);
39445         }
39446         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39447     }
39448 });
39449
39450
39451
39452
39453
39454 Roo.bootstrap.layout.South = function(config){
39455     config.region = 'south';
39456     config.cursor = 's-resize';
39457     Roo.bootstrap.layout.Split.call(this, config);
39458     if(this.split){
39459         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39460         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39461         this.split.el.addClass("roo-layout-split-v");
39462     }
39463     
39464 };
39465
39466 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39467     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39468     
39469     onRender : function(ctr, pos)
39470     {
39471         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39472         var size = this.config.initialSize || this.config.height;
39473         if(this.el && typeof size != "undefined"){
39474             this.el.setHeight(size);
39475         }
39476     
39477     },
39478     
39479     getBox : function(){
39480         if(this.collapsed){
39481             return this.collapsedEl.getBox();
39482         }
39483         var box = this.el.getBox();
39484         if(this.split){
39485             var sh = this.split.el.getHeight();
39486             box.height += sh;
39487             box.y -= sh;
39488         }
39489         return box;
39490     },
39491     
39492     updateBox : function(box){
39493         if(this.split && !this.collapsed){
39494             var sh = this.split.el.getHeight();
39495             box.height -= sh;
39496             box.y += sh;
39497             this.split.el.setLeft(box.x);
39498             this.split.el.setTop(box.y-sh);
39499             this.split.el.setWidth(box.width);
39500         }
39501         if(this.collapsed){
39502             this.updateBody(box.width, null);
39503         }
39504         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39505     }
39506 });
39507
39508 Roo.bootstrap.layout.East = function(config){
39509     config.region = "east";
39510     config.cursor = "e-resize";
39511     Roo.bootstrap.layout.Split.call(this, config);
39512     if(this.split){
39513         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39514         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39515         this.split.el.addClass("roo-layout-split-h");
39516     }
39517     
39518 };
39519 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39520     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39521     
39522     onRender : function(ctr, pos)
39523     {
39524         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39525         var size = this.config.initialSize || this.config.width;
39526         if(this.el && typeof size != "undefined"){
39527             this.el.setWidth(size);
39528         }
39529     
39530     },
39531     
39532     getBox : function(){
39533         if(this.collapsed){
39534             return this.collapsedEl.getBox();
39535         }
39536         var box = this.el.getBox();
39537         if(this.split){
39538             var sw = this.split.el.getWidth();
39539             box.width += sw;
39540             box.x -= sw;
39541         }
39542         return box;
39543     },
39544
39545     updateBox : function(box){
39546         if(this.split && !this.collapsed){
39547             var sw = this.split.el.getWidth();
39548             box.width -= sw;
39549             this.split.el.setLeft(box.x);
39550             this.split.el.setTop(box.y);
39551             this.split.el.setHeight(box.height);
39552             box.x += sw;
39553         }
39554         if(this.collapsed){
39555             this.updateBody(null, box.height);
39556         }
39557         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39558     }
39559 });
39560
39561 Roo.bootstrap.layout.West = function(config){
39562     config.region = "west";
39563     config.cursor = "w-resize";
39564     
39565     Roo.bootstrap.layout.Split.call(this, config);
39566     if(this.split){
39567         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39568         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39569         this.split.el.addClass("roo-layout-split-h");
39570     }
39571     
39572 };
39573 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39574     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39575     
39576     onRender: function(ctr, pos)
39577     {
39578         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39579         var size = this.config.initialSize || this.config.width;
39580         if(typeof size != "undefined"){
39581             this.el.setWidth(size);
39582         }
39583     },
39584     
39585     getBox : function(){
39586         if(this.collapsed){
39587             return this.collapsedEl.getBox();
39588         }
39589         var box = this.el.getBox();
39590         if (box.width == 0) {
39591             box.width = this.config.width; // kludge?
39592         }
39593         if(this.split){
39594             box.width += this.split.el.getWidth();
39595         }
39596         return box;
39597     },
39598     
39599     updateBox : function(box){
39600         if(this.split && !this.collapsed){
39601             var sw = this.split.el.getWidth();
39602             box.width -= sw;
39603             this.split.el.setLeft(box.x+box.width);
39604             this.split.el.setTop(box.y);
39605             this.split.el.setHeight(box.height);
39606         }
39607         if(this.collapsed){
39608             this.updateBody(null, box.height);
39609         }
39610         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39611     }
39612 });Roo.namespace("Roo.bootstrap.panel");/*
39613  * Based on:
39614  * Ext JS Library 1.1.1
39615  * Copyright(c) 2006-2007, Ext JS, LLC.
39616  *
39617  * Originally Released Under LGPL - original licence link has changed is not relivant.
39618  *
39619  * Fork - LGPL
39620  * <script type="text/javascript">
39621  */
39622 /**
39623  * @class Roo.ContentPanel
39624  * @extends Roo.util.Observable
39625  * A basic ContentPanel element.
39626  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39627  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39628  * @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
39629  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39630  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39631  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39632  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39633  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39634  * @cfg {String} title          The title for this panel
39635  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39636  * @cfg {String} url            Calls {@link #setUrl} with this value
39637  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39638  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39639  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39640  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39641  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
39642  * @cfg {Boolean} badges render the badges
39643  * @cfg {String} cls  extra classes to use  
39644  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39645
39646  * @constructor
39647  * Create a new ContentPanel.
39648  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39649  * @param {String/Object} config A string to set only the title or a config object
39650  * @param {String} content (optional) Set the HTML content for this panel
39651  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39652  */
39653 Roo.bootstrap.panel.Content = function( config){
39654     
39655     this.tpl = config.tpl || false;
39656     
39657     var el = config.el;
39658     var content = config.content;
39659
39660     if(config.autoCreate){ // xtype is available if this is called from factory
39661         el = Roo.id();
39662     }
39663     this.el = Roo.get(el);
39664     if(!this.el && config && config.autoCreate){
39665         if(typeof config.autoCreate == "object"){
39666             if(!config.autoCreate.id){
39667                 config.autoCreate.id = config.id||el;
39668             }
39669             this.el = Roo.DomHelper.append(document.body,
39670                         config.autoCreate, true);
39671         }else{
39672             var elcfg =  {
39673                 tag: "div",
39674                 cls: (config.cls || '') +
39675                     (config.background ? ' bg-' + config.background : '') +
39676                     " roo-layout-inactive-content",
39677                 id: config.id||el
39678             };
39679             if (config.iframe) {
39680                 elcfg.cn = [
39681                     {
39682                         tag : 'iframe',
39683                         style : 'border: 0px',
39684                         src : 'about:blank'
39685                     }
39686                 ];
39687             }
39688               
39689             if (config.html) {
39690                 elcfg.html = config.html;
39691                 
39692             }
39693                         
39694             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39695             if (config.iframe) {
39696                 this.iframeEl = this.el.select('iframe',true).first();
39697             }
39698             
39699         }
39700     } 
39701     this.closable = false;
39702     this.loaded = false;
39703     this.active = false;
39704    
39705       
39706     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39707         
39708         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39709         
39710         this.wrapEl = this.el; //this.el.wrap();
39711         var ti = [];
39712         if (config.toolbar.items) {
39713             ti = config.toolbar.items ;
39714             delete config.toolbar.items ;
39715         }
39716         
39717         var nitems = [];
39718         this.toolbar.render(this.wrapEl, 'before');
39719         for(var i =0;i < ti.length;i++) {
39720           //  Roo.log(['add child', items[i]]);
39721             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39722         }
39723         this.toolbar.items = nitems;
39724         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39725         delete config.toolbar;
39726         
39727     }
39728     /*
39729     // xtype created footer. - not sure if will work as we normally have to render first..
39730     if (this.footer && !this.footer.el && this.footer.xtype) {
39731         if (!this.wrapEl) {
39732             this.wrapEl = this.el.wrap();
39733         }
39734     
39735         this.footer.container = this.wrapEl.createChild();
39736          
39737         this.footer = Roo.factory(this.footer, Roo);
39738         
39739     }
39740     */
39741     
39742      if(typeof config == "string"){
39743         this.title = config;
39744     }else{
39745         Roo.apply(this, config);
39746     }
39747     
39748     if(this.resizeEl){
39749         this.resizeEl = Roo.get(this.resizeEl, true);
39750     }else{
39751         this.resizeEl = this.el;
39752     }
39753     // handle view.xtype
39754     
39755  
39756     
39757     
39758     this.addEvents({
39759         /**
39760          * @event activate
39761          * Fires when this panel is activated. 
39762          * @param {Roo.ContentPanel} this
39763          */
39764         "activate" : true,
39765         /**
39766          * @event deactivate
39767          * Fires when this panel is activated. 
39768          * @param {Roo.ContentPanel} this
39769          */
39770         "deactivate" : true,
39771
39772         /**
39773          * @event resize
39774          * Fires when this panel is resized if fitToFrame is true.
39775          * @param {Roo.ContentPanel} this
39776          * @param {Number} width The width after any component adjustments
39777          * @param {Number} height The height after any component adjustments
39778          */
39779         "resize" : true,
39780         
39781          /**
39782          * @event render
39783          * Fires when this tab is created
39784          * @param {Roo.ContentPanel} this
39785          */
39786         "render" : true
39787         
39788         
39789         
39790     });
39791     
39792
39793     
39794     
39795     if(this.autoScroll && !this.iframe){
39796         this.resizeEl.setStyle("overflow", "auto");
39797     } else {
39798         // fix randome scrolling
39799         //this.el.on('scroll', function() {
39800         //    Roo.log('fix random scolling');
39801         //    this.scrollTo('top',0); 
39802         //});
39803     }
39804     content = content || this.content;
39805     if(content){
39806         this.setContent(content);
39807     }
39808     if(config && config.url){
39809         this.setUrl(this.url, this.params, this.loadOnce);
39810     }
39811     
39812     
39813     
39814     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39815     
39816     if (this.view && typeof(this.view.xtype) != 'undefined') {
39817         this.view.el = this.el.appendChild(document.createElement("div"));
39818         this.view = Roo.factory(this.view); 
39819         this.view.render  &&  this.view.render(false, '');  
39820     }
39821     
39822     
39823     this.fireEvent('render', this);
39824 };
39825
39826 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39827     
39828     cls : '',
39829     background : '',
39830     
39831     tabTip : '',
39832     
39833     iframe : false,
39834     iframeEl : false,
39835     
39836     setRegion : function(region){
39837         this.region = region;
39838         this.setActiveClass(region && !this.background);
39839     },
39840     
39841     
39842     setActiveClass: function(state)
39843     {
39844         if(state){
39845            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39846            this.el.setStyle('position','relative');
39847         }else{
39848            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39849            this.el.setStyle('position', 'absolute');
39850         } 
39851     },
39852     
39853     /**
39854      * Returns the toolbar for this Panel if one was configured. 
39855      * @return {Roo.Toolbar} 
39856      */
39857     getToolbar : function(){
39858         return this.toolbar;
39859     },
39860     
39861     setActiveState : function(active)
39862     {
39863         this.active = active;
39864         this.setActiveClass(active);
39865         if(!active){
39866             if(this.fireEvent("deactivate", this) === false){
39867                 return false;
39868             }
39869             return true;
39870         }
39871         this.fireEvent("activate", this);
39872         return true;
39873     },
39874     /**
39875      * Updates this panel's element (not for iframe)
39876      * @param {String} content The new content
39877      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39878     */
39879     setContent : function(content, loadScripts){
39880         if (this.iframe) {
39881             return;
39882         }
39883         
39884         this.el.update(content, loadScripts);
39885     },
39886
39887     ignoreResize : function(w, h){
39888         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39889             return true;
39890         }else{
39891             this.lastSize = {width: w, height: h};
39892             return false;
39893         }
39894     },
39895     /**
39896      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39897      * @return {Roo.UpdateManager} The UpdateManager
39898      */
39899     getUpdateManager : function(){
39900         if (this.iframe) {
39901             return false;
39902         }
39903         return this.el.getUpdateManager();
39904     },
39905      /**
39906      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39907      * Does not work with IFRAME contents
39908      * @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:
39909 <pre><code>
39910 panel.load({
39911     url: "your-url.php",
39912     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39913     callback: yourFunction,
39914     scope: yourObject, //(optional scope)
39915     discardUrl: false,
39916     nocache: false,
39917     text: "Loading...",
39918     timeout: 30,
39919     scripts: false
39920 });
39921 </code></pre>
39922      
39923      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39924      * 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.
39925      * @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}
39926      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39927      * @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.
39928      * @return {Roo.ContentPanel} this
39929      */
39930     load : function(){
39931         
39932         if (this.iframe) {
39933             return this;
39934         }
39935         
39936         var um = this.el.getUpdateManager();
39937         um.update.apply(um, arguments);
39938         return this;
39939     },
39940
39941
39942     /**
39943      * 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.
39944      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39945      * @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)
39946      * @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)
39947      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
39948      */
39949     setUrl : function(url, params, loadOnce){
39950         if (this.iframe) {
39951             this.iframeEl.dom.src = url;
39952             return false;
39953         }
39954         
39955         if(this.refreshDelegate){
39956             this.removeListener("activate", this.refreshDelegate);
39957         }
39958         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39959         this.on("activate", this.refreshDelegate);
39960         return this.el.getUpdateManager();
39961     },
39962     
39963     _handleRefresh : function(url, params, loadOnce){
39964         if(!loadOnce || !this.loaded){
39965             var updater = this.el.getUpdateManager();
39966             updater.update(url, params, this._setLoaded.createDelegate(this));
39967         }
39968     },
39969     
39970     _setLoaded : function(){
39971         this.loaded = true;
39972     }, 
39973     
39974     /**
39975      * Returns this panel's id
39976      * @return {String} 
39977      */
39978     getId : function(){
39979         return this.el.id;
39980     },
39981     
39982     /** 
39983      * Returns this panel's element - used by regiosn to add.
39984      * @return {Roo.Element} 
39985      */
39986     getEl : function(){
39987         return this.wrapEl || this.el;
39988     },
39989     
39990    
39991     
39992     adjustForComponents : function(width, height)
39993     {
39994         //Roo.log('adjustForComponents ');
39995         if(this.resizeEl != this.el){
39996             width -= this.el.getFrameWidth('lr');
39997             height -= this.el.getFrameWidth('tb');
39998         }
39999         if(this.toolbar){
40000             var te = this.toolbar.getEl();
40001             te.setWidth(width);
40002             height -= te.getHeight();
40003         }
40004         if(this.footer){
40005             var te = this.footer.getEl();
40006             te.setWidth(width);
40007             height -= te.getHeight();
40008         }
40009         
40010         
40011         if(this.adjustments){
40012             width += this.adjustments[0];
40013             height += this.adjustments[1];
40014         }
40015         return {"width": width, "height": height};
40016     },
40017     
40018     setSize : function(width, height){
40019         if(this.fitToFrame && !this.ignoreResize(width, height)){
40020             if(this.fitContainer && this.resizeEl != this.el){
40021                 this.el.setSize(width, height);
40022             }
40023             var size = this.adjustForComponents(width, height);
40024             if (this.iframe) {
40025                 this.iframeEl.setSize(width,height);
40026             }
40027             
40028             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40029             this.fireEvent('resize', this, size.width, size.height);
40030             
40031             
40032         }
40033     },
40034     
40035     /**
40036      * Returns this panel's title
40037      * @return {String} 
40038      */
40039     getTitle : function(){
40040         
40041         if (typeof(this.title) != 'object') {
40042             return this.title;
40043         }
40044         
40045         var t = '';
40046         for (var k in this.title) {
40047             if (!this.title.hasOwnProperty(k)) {
40048                 continue;
40049             }
40050             
40051             if (k.indexOf('-') >= 0) {
40052                 var s = k.split('-');
40053                 for (var i = 0; i<s.length; i++) {
40054                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40055                 }
40056             } else {
40057                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40058             }
40059         }
40060         return t;
40061     },
40062     
40063     /**
40064      * Set this panel's title
40065      * @param {String} title
40066      */
40067     setTitle : function(title){
40068         this.title = title;
40069         if(this.region){
40070             this.region.updatePanelTitle(this, title);
40071         }
40072     },
40073     
40074     /**
40075      * Returns true is this panel was configured to be closable
40076      * @return {Boolean} 
40077      */
40078     isClosable : function(){
40079         return this.closable;
40080     },
40081     
40082     beforeSlide : function(){
40083         this.el.clip();
40084         this.resizeEl.clip();
40085     },
40086     
40087     afterSlide : function(){
40088         this.el.unclip();
40089         this.resizeEl.unclip();
40090     },
40091     
40092     /**
40093      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40094      *   Will fail silently if the {@link #setUrl} method has not been called.
40095      *   This does not activate the panel, just updates its content.
40096      */
40097     refresh : function(){
40098         if(this.refreshDelegate){
40099            this.loaded = false;
40100            this.refreshDelegate();
40101         }
40102     },
40103     
40104     /**
40105      * Destroys this panel
40106      */
40107     destroy : function(){
40108         this.el.removeAllListeners();
40109         var tempEl = document.createElement("span");
40110         tempEl.appendChild(this.el.dom);
40111         tempEl.innerHTML = "";
40112         this.el.remove();
40113         this.el = null;
40114     },
40115     
40116     /**
40117      * form - if the content panel contains a form - this is a reference to it.
40118      * @type {Roo.form.Form}
40119      */
40120     form : false,
40121     /**
40122      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40123      *    This contains a reference to it.
40124      * @type {Roo.View}
40125      */
40126     view : false,
40127     
40128       /**
40129      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40130      * <pre><code>
40131
40132 layout.addxtype({
40133        xtype : 'Form',
40134        items: [ .... ]
40135    }
40136 );
40137
40138 </code></pre>
40139      * @param {Object} cfg Xtype definition of item to add.
40140      */
40141     
40142     
40143     getChildContainer: function () {
40144         return this.getEl();
40145     }
40146     
40147     
40148     /*
40149         var  ret = new Roo.factory(cfg);
40150         return ret;
40151         
40152         
40153         // add form..
40154         if (cfg.xtype.match(/^Form$/)) {
40155             
40156             var el;
40157             //if (this.footer) {
40158             //    el = this.footer.container.insertSibling(false, 'before');
40159             //} else {
40160                 el = this.el.createChild();
40161             //}
40162
40163             this.form = new  Roo.form.Form(cfg);
40164             
40165             
40166             if ( this.form.allItems.length) {
40167                 this.form.render(el.dom);
40168             }
40169             return this.form;
40170         }
40171         // should only have one of theses..
40172         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40173             // views.. should not be just added - used named prop 'view''
40174             
40175             cfg.el = this.el.appendChild(document.createElement("div"));
40176             // factory?
40177             
40178             var ret = new Roo.factory(cfg);
40179              
40180              ret.render && ret.render(false, ''); // render blank..
40181             this.view = ret;
40182             return ret;
40183         }
40184         return false;
40185     }
40186     \*/
40187 });
40188  
40189 /**
40190  * @class Roo.bootstrap.panel.Grid
40191  * @extends Roo.bootstrap.panel.Content
40192  * @constructor
40193  * Create a new GridPanel.
40194  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40195  * @param {Object} config A the config object
40196   
40197  */
40198
40199
40200
40201 Roo.bootstrap.panel.Grid = function(config)
40202 {
40203     
40204       
40205     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40206         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40207
40208     config.el = this.wrapper;
40209     //this.el = this.wrapper;
40210     
40211       if (config.container) {
40212         // ctor'ed from a Border/panel.grid
40213         
40214         
40215         this.wrapper.setStyle("overflow", "hidden");
40216         this.wrapper.addClass('roo-grid-container');
40217
40218     }
40219     
40220     
40221     if(config.toolbar){
40222         var tool_el = this.wrapper.createChild();    
40223         this.toolbar = Roo.factory(config.toolbar);
40224         var ti = [];
40225         if (config.toolbar.items) {
40226             ti = config.toolbar.items ;
40227             delete config.toolbar.items ;
40228         }
40229         
40230         var nitems = [];
40231         this.toolbar.render(tool_el);
40232         for(var i =0;i < ti.length;i++) {
40233           //  Roo.log(['add child', items[i]]);
40234             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40235         }
40236         this.toolbar.items = nitems;
40237         
40238         delete config.toolbar;
40239     }
40240     
40241     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40242     config.grid.scrollBody = true;;
40243     config.grid.monitorWindowResize = false; // turn off autosizing
40244     config.grid.autoHeight = false;
40245     config.grid.autoWidth = false;
40246     
40247     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40248     
40249     if (config.background) {
40250         // render grid on panel activation (if panel background)
40251         this.on('activate', function(gp) {
40252             if (!gp.grid.rendered) {
40253                 gp.grid.render(this.wrapper);
40254                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40255             }
40256         });
40257             
40258     } else {
40259         this.grid.render(this.wrapper);
40260         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40261
40262     }
40263     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40264     // ??? needed ??? config.el = this.wrapper;
40265     
40266     
40267     
40268   
40269     // xtype created footer. - not sure if will work as we normally have to render first..
40270     if (this.footer && !this.footer.el && this.footer.xtype) {
40271         
40272         var ctr = this.grid.getView().getFooterPanel(true);
40273         this.footer.dataSource = this.grid.dataSource;
40274         this.footer = Roo.factory(this.footer, Roo);
40275         this.footer.render(ctr);
40276         
40277     }
40278     
40279     
40280     
40281     
40282      
40283 };
40284
40285 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40286     getId : function(){
40287         return this.grid.id;
40288     },
40289     
40290     /**
40291      * Returns the grid for this panel
40292      * @return {Roo.bootstrap.Table} 
40293      */
40294     getGrid : function(){
40295         return this.grid;    
40296     },
40297     
40298     setSize : function(width, height){
40299         if(!this.ignoreResize(width, height)){
40300             var grid = this.grid;
40301             var size = this.adjustForComponents(width, height);
40302             // tfoot is not a footer?
40303           
40304             
40305             var gridel = grid.getGridEl();
40306             gridel.setSize(size.width, size.height);
40307             
40308             var tbd = grid.getGridEl().select('tbody', true).first();
40309             var thd = grid.getGridEl().select('thead',true).first();
40310             var tbf= grid.getGridEl().select('tfoot', true).first();
40311
40312             if (tbf) {
40313                 size.height -= tbf.getHeight();
40314             }
40315             if (thd) {
40316                 size.height -= thd.getHeight();
40317             }
40318             
40319             tbd.setSize(size.width, size.height );
40320             // this is for the account management tab -seems to work there.
40321             var thd = grid.getGridEl().select('thead',true).first();
40322             //if (tbd) {
40323             //    tbd.setSize(size.width, size.height - thd.getHeight());
40324             //}
40325              
40326             grid.autoSize();
40327         }
40328     },
40329      
40330     
40331     
40332     beforeSlide : function(){
40333         this.grid.getView().scroller.clip();
40334     },
40335     
40336     afterSlide : function(){
40337         this.grid.getView().scroller.unclip();
40338     },
40339     
40340     destroy : function(){
40341         this.grid.destroy();
40342         delete this.grid;
40343         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40344     }
40345 });
40346
40347 /**
40348  * @class Roo.bootstrap.panel.Nest
40349  * @extends Roo.bootstrap.panel.Content
40350  * @constructor
40351  * Create a new Panel, that can contain a layout.Border.
40352  * 
40353  * 
40354  * @param {Roo.BorderLayout} layout The layout for this panel
40355  * @param {String/Object} config A string to set only the title or a config object
40356  */
40357 Roo.bootstrap.panel.Nest = function(config)
40358 {
40359     // construct with only one argument..
40360     /* FIXME - implement nicer consturctors
40361     if (layout.layout) {
40362         config = layout;
40363         layout = config.layout;
40364         delete config.layout;
40365     }
40366     if (layout.xtype && !layout.getEl) {
40367         // then layout needs constructing..
40368         layout = Roo.factory(layout, Roo);
40369     }
40370     */
40371     
40372     config.el =  config.layout.getEl();
40373     
40374     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40375     
40376     config.layout.monitorWindowResize = false; // turn off autosizing
40377     this.layout = config.layout;
40378     this.layout.getEl().addClass("roo-layout-nested-layout");
40379     this.layout.parent = this;
40380     
40381     
40382     
40383     
40384 };
40385
40386 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40387
40388     setSize : function(width, height){
40389         if(!this.ignoreResize(width, height)){
40390             var size = this.adjustForComponents(width, height);
40391             var el = this.layout.getEl();
40392             if (size.height < 1) {
40393                 el.setWidth(size.width);   
40394             } else {
40395                 el.setSize(size.width, size.height);
40396             }
40397             var touch = el.dom.offsetWidth;
40398             this.layout.layout();
40399             // ie requires a double layout on the first pass
40400             if(Roo.isIE && !this.initialized){
40401                 this.initialized = true;
40402                 this.layout.layout();
40403             }
40404         }
40405     },
40406     
40407     // activate all subpanels if not currently active..
40408     
40409     setActiveState : function(active){
40410         this.active = active;
40411         this.setActiveClass(active);
40412         
40413         if(!active){
40414             this.fireEvent("deactivate", this);
40415             return;
40416         }
40417         
40418         this.fireEvent("activate", this);
40419         // not sure if this should happen before or after..
40420         if (!this.layout) {
40421             return; // should not happen..
40422         }
40423         var reg = false;
40424         for (var r in this.layout.regions) {
40425             reg = this.layout.getRegion(r);
40426             if (reg.getActivePanel()) {
40427                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40428                 reg.setActivePanel(reg.getActivePanel());
40429                 continue;
40430             }
40431             if (!reg.panels.length) {
40432                 continue;
40433             }
40434             reg.showPanel(reg.getPanel(0));
40435         }
40436         
40437         
40438         
40439         
40440     },
40441     
40442     /**
40443      * Returns the nested BorderLayout for this panel
40444      * @return {Roo.BorderLayout} 
40445      */
40446     getLayout : function(){
40447         return this.layout;
40448     },
40449     
40450      /**
40451      * Adds a xtype elements to the layout of the nested panel
40452      * <pre><code>
40453
40454 panel.addxtype({
40455        xtype : 'ContentPanel',
40456        region: 'west',
40457        items: [ .... ]
40458    }
40459 );
40460
40461 panel.addxtype({
40462         xtype : 'NestedLayoutPanel',
40463         region: 'west',
40464         layout: {
40465            center: { },
40466            west: { }   
40467         },
40468         items : [ ... list of content panels or nested layout panels.. ]
40469    }
40470 );
40471 </code></pre>
40472      * @param {Object} cfg Xtype definition of item to add.
40473      */
40474     addxtype : function(cfg) {
40475         return this.layout.addxtype(cfg);
40476     
40477     }
40478 });/*
40479  * Based on:
40480  * Ext JS Library 1.1.1
40481  * Copyright(c) 2006-2007, Ext JS, LLC.
40482  *
40483  * Originally Released Under LGPL - original licence link has changed is not relivant.
40484  *
40485  * Fork - LGPL
40486  * <script type="text/javascript">
40487  */
40488 /**
40489  * @class Roo.TabPanel
40490  * @extends Roo.util.Observable
40491  * A lightweight tab container.
40492  * <br><br>
40493  * Usage:
40494  * <pre><code>
40495 // basic tabs 1, built from existing content
40496 var tabs = new Roo.TabPanel("tabs1");
40497 tabs.addTab("script", "View Script");
40498 tabs.addTab("markup", "View Markup");
40499 tabs.activate("script");
40500
40501 // more advanced tabs, built from javascript
40502 var jtabs = new Roo.TabPanel("jtabs");
40503 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40504
40505 // set up the UpdateManager
40506 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40507 var updater = tab2.getUpdateManager();
40508 updater.setDefaultUrl("ajax1.htm");
40509 tab2.on('activate', updater.refresh, updater, true);
40510
40511 // Use setUrl for Ajax loading
40512 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40513 tab3.setUrl("ajax2.htm", null, true);
40514
40515 // Disabled tab
40516 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40517 tab4.disable();
40518
40519 jtabs.activate("jtabs-1");
40520  * </code></pre>
40521  * @constructor
40522  * Create a new TabPanel.
40523  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40524  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40525  */
40526 Roo.bootstrap.panel.Tabs = function(config){
40527     /**
40528     * The container element for this TabPanel.
40529     * @type Roo.Element
40530     */
40531     this.el = Roo.get(config.el);
40532     delete config.el;
40533     if(config){
40534         if(typeof config == "boolean"){
40535             this.tabPosition = config ? "bottom" : "top";
40536         }else{
40537             Roo.apply(this, config);
40538         }
40539     }
40540     
40541     if(this.tabPosition == "bottom"){
40542         // if tabs are at the bottom = create the body first.
40543         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40544         this.el.addClass("roo-tabs-bottom");
40545     }
40546     // next create the tabs holders
40547     
40548     if (this.tabPosition == "west"){
40549         
40550         var reg = this.region; // fake it..
40551         while (reg) {
40552             if (!reg.mgr.parent) {
40553                 break;
40554             }
40555             reg = reg.mgr.parent.region;
40556         }
40557         Roo.log("got nest?");
40558         Roo.log(reg);
40559         if (reg.mgr.getRegion('west')) {
40560             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40561             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40562             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40563             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40564             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40565         
40566             
40567         }
40568         
40569         
40570     } else {
40571      
40572         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40573         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40574         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40575         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40576     }
40577     
40578     
40579     if(Roo.isIE){
40580         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40581     }
40582     
40583     // finally - if tabs are at the top, then create the body last..
40584     if(this.tabPosition != "bottom"){
40585         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40586          * @type Roo.Element
40587          */
40588         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40589         this.el.addClass("roo-tabs-top");
40590     }
40591     this.items = [];
40592
40593     this.bodyEl.setStyle("position", "relative");
40594
40595     this.active = null;
40596     this.activateDelegate = this.activate.createDelegate(this);
40597
40598     this.addEvents({
40599         /**
40600          * @event tabchange
40601          * Fires when the active tab changes
40602          * @param {Roo.TabPanel} this
40603          * @param {Roo.TabPanelItem} activePanel The new active tab
40604          */
40605         "tabchange": true,
40606         /**
40607          * @event beforetabchange
40608          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40609          * @param {Roo.TabPanel} this
40610          * @param {Object} e Set cancel to true on this object to cancel the tab change
40611          * @param {Roo.TabPanelItem} tab The tab being changed to
40612          */
40613         "beforetabchange" : true
40614     });
40615
40616     Roo.EventManager.onWindowResize(this.onResize, this);
40617     this.cpad = this.el.getPadding("lr");
40618     this.hiddenCount = 0;
40619
40620
40621     // toolbar on the tabbar support...
40622     if (this.toolbar) {
40623         alert("no toolbar support yet");
40624         this.toolbar  = false;
40625         /*
40626         var tcfg = this.toolbar;
40627         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40628         this.toolbar = new Roo.Toolbar(tcfg);
40629         if (Roo.isSafari) {
40630             var tbl = tcfg.container.child('table', true);
40631             tbl.setAttribute('width', '100%');
40632         }
40633         */
40634         
40635     }
40636    
40637
40638
40639     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40640 };
40641
40642 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40643     /*
40644      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40645      */
40646     tabPosition : "top",
40647     /*
40648      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40649      */
40650     currentTabWidth : 0,
40651     /*
40652      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40653      */
40654     minTabWidth : 40,
40655     /*
40656      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40657      */
40658     maxTabWidth : 250,
40659     /*
40660      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40661      */
40662     preferredTabWidth : 175,
40663     /*
40664      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40665      */
40666     resizeTabs : false,
40667     /*
40668      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40669      */
40670     monitorResize : true,
40671     /*
40672      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40673      */
40674     toolbar : false,  // set by caller..
40675     
40676     region : false, /// set by caller
40677     
40678     disableTooltips : true, // not used yet...
40679
40680     /**
40681      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40682      * @param {String} id The id of the div to use <b>or create</b>
40683      * @param {String} text The text for the tab
40684      * @param {String} content (optional) Content to put in the TabPanelItem body
40685      * @param {Boolean} closable (optional) True to create a close icon on the tab
40686      * @return {Roo.TabPanelItem} The created TabPanelItem
40687      */
40688     addTab : function(id, text, content, closable, tpl)
40689     {
40690         var item = new Roo.bootstrap.panel.TabItem({
40691             panel: this,
40692             id : id,
40693             text : text,
40694             closable : closable,
40695             tpl : tpl
40696         });
40697         this.addTabItem(item);
40698         if(content){
40699             item.setContent(content);
40700         }
40701         return item;
40702     },
40703
40704     /**
40705      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40706      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40707      * @return {Roo.TabPanelItem}
40708      */
40709     getTab : function(id){
40710         return this.items[id];
40711     },
40712
40713     /**
40714      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40715      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40716      */
40717     hideTab : function(id){
40718         var t = this.items[id];
40719         if(!t.isHidden()){
40720            t.setHidden(true);
40721            this.hiddenCount++;
40722            this.autoSizeTabs();
40723         }
40724     },
40725
40726     /**
40727      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40728      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40729      */
40730     unhideTab : function(id){
40731         var t = this.items[id];
40732         if(t.isHidden()){
40733            t.setHidden(false);
40734            this.hiddenCount--;
40735            this.autoSizeTabs();
40736         }
40737     },
40738
40739     /**
40740      * Adds an existing {@link Roo.TabPanelItem}.
40741      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40742      */
40743     addTabItem : function(item)
40744     {
40745         this.items[item.id] = item;
40746         this.items.push(item);
40747         this.autoSizeTabs();
40748       //  if(this.resizeTabs){
40749     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40750   //         this.autoSizeTabs();
40751 //        }else{
40752 //            item.autoSize();
40753        // }
40754     },
40755
40756     /**
40757      * Removes a {@link Roo.TabPanelItem}.
40758      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40759      */
40760     removeTab : function(id){
40761         var items = this.items;
40762         var tab = items[id];
40763         if(!tab) { return; }
40764         var index = items.indexOf(tab);
40765         if(this.active == tab && items.length > 1){
40766             var newTab = this.getNextAvailable(index);
40767             if(newTab) {
40768                 newTab.activate();
40769             }
40770         }
40771         this.stripEl.dom.removeChild(tab.pnode.dom);
40772         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40773             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40774         }
40775         items.splice(index, 1);
40776         delete this.items[tab.id];
40777         tab.fireEvent("close", tab);
40778         tab.purgeListeners();
40779         this.autoSizeTabs();
40780     },
40781
40782     getNextAvailable : function(start){
40783         var items = this.items;
40784         var index = start;
40785         // look for a next tab that will slide over to
40786         // replace the one being removed
40787         while(index < items.length){
40788             var item = items[++index];
40789             if(item && !item.isHidden()){
40790                 return item;
40791             }
40792         }
40793         // if one isn't found select the previous tab (on the left)
40794         index = start;
40795         while(index >= 0){
40796             var item = items[--index];
40797             if(item && !item.isHidden()){
40798                 return item;
40799             }
40800         }
40801         return null;
40802     },
40803
40804     /**
40805      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40806      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40807      */
40808     disableTab : function(id){
40809         var tab = this.items[id];
40810         if(tab && this.active != tab){
40811             tab.disable();
40812         }
40813     },
40814
40815     /**
40816      * Enables a {@link Roo.TabPanelItem} that is disabled.
40817      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40818      */
40819     enableTab : function(id){
40820         var tab = this.items[id];
40821         tab.enable();
40822     },
40823
40824     /**
40825      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40826      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40827      * @return {Roo.TabPanelItem} The TabPanelItem.
40828      */
40829     activate : function(id)
40830     {
40831         //Roo.log('activite:'  + id);
40832         
40833         var tab = this.items[id];
40834         if(!tab){
40835             return null;
40836         }
40837         if(tab == this.active || tab.disabled){
40838             return tab;
40839         }
40840         var e = {};
40841         this.fireEvent("beforetabchange", this, e, tab);
40842         if(e.cancel !== true && !tab.disabled){
40843             if(this.active){
40844                 this.active.hide();
40845             }
40846             this.active = this.items[id];
40847             this.active.show();
40848             this.fireEvent("tabchange", this, this.active);
40849         }
40850         return tab;
40851     },
40852
40853     /**
40854      * Gets the active {@link Roo.TabPanelItem}.
40855      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40856      */
40857     getActiveTab : function(){
40858         return this.active;
40859     },
40860
40861     /**
40862      * Updates the tab body element to fit the height of the container element
40863      * for overflow scrolling
40864      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40865      */
40866     syncHeight : function(targetHeight){
40867         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40868         var bm = this.bodyEl.getMargins();
40869         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40870         this.bodyEl.setHeight(newHeight);
40871         return newHeight;
40872     },
40873
40874     onResize : function(){
40875         if(this.monitorResize){
40876             this.autoSizeTabs();
40877         }
40878     },
40879
40880     /**
40881      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40882      */
40883     beginUpdate : function(){
40884         this.updating = true;
40885     },
40886
40887     /**
40888      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40889      */
40890     endUpdate : function(){
40891         this.updating = false;
40892         this.autoSizeTabs();
40893     },
40894
40895     /**
40896      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40897      */
40898     autoSizeTabs : function()
40899     {
40900         var count = this.items.length;
40901         var vcount = count - this.hiddenCount;
40902         
40903         if (vcount < 2) {
40904             this.stripEl.hide();
40905         } else {
40906             this.stripEl.show();
40907         }
40908         
40909         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40910             return;
40911         }
40912         
40913         
40914         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40915         var availWidth = Math.floor(w / vcount);
40916         var b = this.stripBody;
40917         if(b.getWidth() > w){
40918             var tabs = this.items;
40919             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40920             if(availWidth < this.minTabWidth){
40921                 /*if(!this.sleft){    // incomplete scrolling code
40922                     this.createScrollButtons();
40923                 }
40924                 this.showScroll();
40925                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40926             }
40927         }else{
40928             if(this.currentTabWidth < this.preferredTabWidth){
40929                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40930             }
40931         }
40932     },
40933
40934     /**
40935      * Returns the number of tabs in this TabPanel.
40936      * @return {Number}
40937      */
40938      getCount : function(){
40939          return this.items.length;
40940      },
40941
40942     /**
40943      * Resizes all the tabs to the passed width
40944      * @param {Number} The new width
40945      */
40946     setTabWidth : function(width){
40947         this.currentTabWidth = width;
40948         for(var i = 0, len = this.items.length; i < len; i++) {
40949                 if(!this.items[i].isHidden()) {
40950                 this.items[i].setWidth(width);
40951             }
40952         }
40953     },
40954
40955     /**
40956      * Destroys this TabPanel
40957      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40958      */
40959     destroy : function(removeEl){
40960         Roo.EventManager.removeResizeListener(this.onResize, this);
40961         for(var i = 0, len = this.items.length; i < len; i++){
40962             this.items[i].purgeListeners();
40963         }
40964         if(removeEl === true){
40965             this.el.update("");
40966             this.el.remove();
40967         }
40968     },
40969     
40970     createStrip : function(container)
40971     {
40972         var strip = document.createElement("nav");
40973         strip.className = Roo.bootstrap.version == 4 ?
40974             "navbar-light bg-light" : 
40975             "navbar navbar-default"; //"x-tabs-wrap";
40976         container.appendChild(strip);
40977         return strip;
40978     },
40979     
40980     createStripList : function(strip)
40981     {
40982         // div wrapper for retard IE
40983         // returns the "tr" element.
40984         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40985         //'<div class="x-tabs-strip-wrap">'+
40986           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40987           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40988         return strip.firstChild; //.firstChild.firstChild.firstChild;
40989     },
40990     createBody : function(container)
40991     {
40992         var body = document.createElement("div");
40993         Roo.id(body, "tab-body");
40994         //Roo.fly(body).addClass("x-tabs-body");
40995         Roo.fly(body).addClass("tab-content");
40996         container.appendChild(body);
40997         return body;
40998     },
40999     createItemBody :function(bodyEl, id){
41000         var body = Roo.getDom(id);
41001         if(!body){
41002             body = document.createElement("div");
41003             body.id = id;
41004         }
41005         //Roo.fly(body).addClass("x-tabs-item-body");
41006         Roo.fly(body).addClass("tab-pane");
41007          bodyEl.insertBefore(body, bodyEl.firstChild);
41008         return body;
41009     },
41010     /** @private */
41011     createStripElements :  function(stripEl, text, closable, tpl)
41012     {
41013         var td = document.createElement("li"); // was td..
41014         td.className = 'nav-item';
41015         
41016         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41017         
41018         
41019         stripEl.appendChild(td);
41020         /*if(closable){
41021             td.className = "x-tabs-closable";
41022             if(!this.closeTpl){
41023                 this.closeTpl = new Roo.Template(
41024                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41025                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41026                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41027                 );
41028             }
41029             var el = this.closeTpl.overwrite(td, {"text": text});
41030             var close = el.getElementsByTagName("div")[0];
41031             var inner = el.getElementsByTagName("em")[0];
41032             return {"el": el, "close": close, "inner": inner};
41033         } else {
41034         */
41035         // not sure what this is..
41036 //            if(!this.tabTpl){
41037                 //this.tabTpl = new Roo.Template(
41038                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41039                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41040                 //);
41041 //                this.tabTpl = new Roo.Template(
41042 //                   '<a href="#">' +
41043 //                   '<span unselectable="on"' +
41044 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41045 //                            ' >{text}</span></a>'
41046 //                );
41047 //                
41048 //            }
41049
41050
41051             var template = tpl || this.tabTpl || false;
41052             
41053             if(!template){
41054                 template =  new Roo.Template(
41055                         Roo.bootstrap.version == 4 ? 
41056                             (
41057                                 '<a class="nav-link" href="#" unselectable="on"' +
41058                                      (this.disableTooltips ? '' : ' title="{text}"') +
41059                                      ' >{text}</a>'
41060                             ) : (
41061                                 '<a class="nav-link" href="#">' +
41062                                 '<span unselectable="on"' +
41063                                          (this.disableTooltips ? '' : ' title="{text}"') +
41064                                     ' >{text}</span></a>'
41065                             )
41066                 );
41067             }
41068             
41069             switch (typeof(template)) {
41070                 case 'object' :
41071                     break;
41072                 case 'string' :
41073                     template = new Roo.Template(template);
41074                     break;
41075                 default :
41076                     break;
41077             }
41078             
41079             var el = template.overwrite(td, {"text": text});
41080             
41081             var inner = el.getElementsByTagName("span")[0];
41082             
41083             return {"el": el, "inner": inner};
41084             
41085     }
41086         
41087     
41088 });
41089
41090 /**
41091  * @class Roo.TabPanelItem
41092  * @extends Roo.util.Observable
41093  * Represents an individual item (tab plus body) in a TabPanel.
41094  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41095  * @param {String} id The id of this TabPanelItem
41096  * @param {String} text The text for the tab of this TabPanelItem
41097  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41098  */
41099 Roo.bootstrap.panel.TabItem = function(config){
41100     /**
41101      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41102      * @type Roo.TabPanel
41103      */
41104     this.tabPanel = config.panel;
41105     /**
41106      * The id for this TabPanelItem
41107      * @type String
41108      */
41109     this.id = config.id;
41110     /** @private */
41111     this.disabled = false;
41112     /** @private */
41113     this.text = config.text;
41114     /** @private */
41115     this.loaded = false;
41116     this.closable = config.closable;
41117
41118     /**
41119      * The body element for this TabPanelItem.
41120      * @type Roo.Element
41121      */
41122     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41123     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41124     this.bodyEl.setStyle("display", "block");
41125     this.bodyEl.setStyle("zoom", "1");
41126     //this.hideAction();
41127
41128     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41129     /** @private */
41130     this.el = Roo.get(els.el);
41131     this.inner = Roo.get(els.inner, true);
41132      this.textEl = Roo.bootstrap.version == 4 ?
41133         this.el : Roo.get(this.el.dom.firstChild, true);
41134
41135     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41136     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41137
41138     
41139 //    this.el.on("mousedown", this.onTabMouseDown, this);
41140     this.el.on("click", this.onTabClick, this);
41141     /** @private */
41142     if(config.closable){
41143         var c = Roo.get(els.close, true);
41144         c.dom.title = this.closeText;
41145         c.addClassOnOver("close-over");
41146         c.on("click", this.closeClick, this);
41147      }
41148
41149     this.addEvents({
41150          /**
41151          * @event activate
41152          * Fires when this tab becomes the active tab.
41153          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41154          * @param {Roo.TabPanelItem} this
41155          */
41156         "activate": true,
41157         /**
41158          * @event beforeclose
41159          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41160          * @param {Roo.TabPanelItem} this
41161          * @param {Object} e Set cancel to true on this object to cancel the close.
41162          */
41163         "beforeclose": true,
41164         /**
41165          * @event close
41166          * Fires when this tab is closed.
41167          * @param {Roo.TabPanelItem} this
41168          */
41169          "close": true,
41170         /**
41171          * @event deactivate
41172          * Fires when this tab is no longer the active tab.
41173          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41174          * @param {Roo.TabPanelItem} this
41175          */
41176          "deactivate" : true
41177     });
41178     this.hidden = false;
41179
41180     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41181 };
41182
41183 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41184            {
41185     purgeListeners : function(){
41186        Roo.util.Observable.prototype.purgeListeners.call(this);
41187        this.el.removeAllListeners();
41188     },
41189     /**
41190      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41191      */
41192     show : function(){
41193         this.status_node.addClass("active");
41194         this.showAction();
41195         if(Roo.isOpera){
41196             this.tabPanel.stripWrap.repaint();
41197         }
41198         this.fireEvent("activate", this.tabPanel, this);
41199     },
41200
41201     /**
41202      * Returns true if this tab is the active tab.
41203      * @return {Boolean}
41204      */
41205     isActive : function(){
41206         return this.tabPanel.getActiveTab() == this;
41207     },
41208
41209     /**
41210      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41211      */
41212     hide : function(){
41213         this.status_node.removeClass("active");
41214         this.hideAction();
41215         this.fireEvent("deactivate", this.tabPanel, this);
41216     },
41217
41218     hideAction : function(){
41219         this.bodyEl.hide();
41220         this.bodyEl.setStyle("position", "absolute");
41221         this.bodyEl.setLeft("-20000px");
41222         this.bodyEl.setTop("-20000px");
41223     },
41224
41225     showAction : function(){
41226         this.bodyEl.setStyle("position", "relative");
41227         this.bodyEl.setTop("");
41228         this.bodyEl.setLeft("");
41229         this.bodyEl.show();
41230     },
41231
41232     /**
41233      * Set the tooltip for the tab.
41234      * @param {String} tooltip The tab's tooltip
41235      */
41236     setTooltip : function(text){
41237         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41238             this.textEl.dom.qtip = text;
41239             this.textEl.dom.removeAttribute('title');
41240         }else{
41241             this.textEl.dom.title = text;
41242         }
41243     },
41244
41245     onTabClick : function(e){
41246         e.preventDefault();
41247         this.tabPanel.activate(this.id);
41248     },
41249
41250     onTabMouseDown : function(e){
41251         e.preventDefault();
41252         this.tabPanel.activate(this.id);
41253     },
41254 /*
41255     getWidth : function(){
41256         return this.inner.getWidth();
41257     },
41258
41259     setWidth : function(width){
41260         var iwidth = width - this.linode.getPadding("lr");
41261         this.inner.setWidth(iwidth);
41262         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41263         this.linode.setWidth(width);
41264     },
41265 */
41266     /**
41267      * Show or hide the tab
41268      * @param {Boolean} hidden True to hide or false to show.
41269      */
41270     setHidden : function(hidden){
41271         this.hidden = hidden;
41272         this.linode.setStyle("display", hidden ? "none" : "");
41273     },
41274
41275     /**
41276      * Returns true if this tab is "hidden"
41277      * @return {Boolean}
41278      */
41279     isHidden : function(){
41280         return this.hidden;
41281     },
41282
41283     /**
41284      * Returns the text for this tab
41285      * @return {String}
41286      */
41287     getText : function(){
41288         return this.text;
41289     },
41290     /*
41291     autoSize : function(){
41292         //this.el.beginMeasure();
41293         this.textEl.setWidth(1);
41294         /*
41295          *  #2804 [new] Tabs in Roojs
41296          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41297          */
41298         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41299         //this.el.endMeasure();
41300     //},
41301
41302     /**
41303      * Sets the text for the tab (Note: this also sets the tooltip text)
41304      * @param {String} text The tab's text and tooltip
41305      */
41306     setText : function(text){
41307         this.text = text;
41308         this.textEl.update(text);
41309         this.setTooltip(text);
41310         //if(!this.tabPanel.resizeTabs){
41311         //    this.autoSize();
41312         //}
41313     },
41314     /**
41315      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41316      */
41317     activate : function(){
41318         this.tabPanel.activate(this.id);
41319     },
41320
41321     /**
41322      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41323      */
41324     disable : function(){
41325         if(this.tabPanel.active != this){
41326             this.disabled = true;
41327             this.status_node.addClass("disabled");
41328         }
41329     },
41330
41331     /**
41332      * Enables this TabPanelItem if it was previously disabled.
41333      */
41334     enable : function(){
41335         this.disabled = false;
41336         this.status_node.removeClass("disabled");
41337     },
41338
41339     /**
41340      * Sets the content for this TabPanelItem.
41341      * @param {String} content The content
41342      * @param {Boolean} loadScripts true to look for and load scripts
41343      */
41344     setContent : function(content, loadScripts){
41345         this.bodyEl.update(content, loadScripts);
41346     },
41347
41348     /**
41349      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41350      * @return {Roo.UpdateManager} The UpdateManager
41351      */
41352     getUpdateManager : function(){
41353         return this.bodyEl.getUpdateManager();
41354     },
41355
41356     /**
41357      * Set a URL to be used to load the content for this TabPanelItem.
41358      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41359      * @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)
41360      * @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)
41361      * @return {Roo.UpdateManager} The UpdateManager
41362      */
41363     setUrl : function(url, params, loadOnce){
41364         if(this.refreshDelegate){
41365             this.un('activate', this.refreshDelegate);
41366         }
41367         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41368         this.on("activate", this.refreshDelegate);
41369         return this.bodyEl.getUpdateManager();
41370     },
41371
41372     /** @private */
41373     _handleRefresh : function(url, params, loadOnce){
41374         if(!loadOnce || !this.loaded){
41375             var updater = this.bodyEl.getUpdateManager();
41376             updater.update(url, params, this._setLoaded.createDelegate(this));
41377         }
41378     },
41379
41380     /**
41381      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41382      *   Will fail silently if the setUrl method has not been called.
41383      *   This does not activate the panel, just updates its content.
41384      */
41385     refresh : function(){
41386         if(this.refreshDelegate){
41387            this.loaded = false;
41388            this.refreshDelegate();
41389         }
41390     },
41391
41392     /** @private */
41393     _setLoaded : function(){
41394         this.loaded = true;
41395     },
41396
41397     /** @private */
41398     closeClick : function(e){
41399         var o = {};
41400         e.stopEvent();
41401         this.fireEvent("beforeclose", this, o);
41402         if(o.cancel !== true){
41403             this.tabPanel.removeTab(this.id);
41404         }
41405     },
41406     /**
41407      * The text displayed in the tooltip for the close icon.
41408      * @type String
41409      */
41410     closeText : "Close this tab"
41411 });
41412 /**
41413 *    This script refer to:
41414 *    Title: International Telephone Input
41415 *    Author: Jack O'Connor
41416 *    Code version:  v12.1.12
41417 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41418 **/
41419
41420 Roo.bootstrap.PhoneInputData = function() {
41421     var d = [
41422       [
41423         "Afghanistan (‫افغانستان‬‎)",
41424         "af",
41425         "93"
41426       ],
41427       [
41428         "Albania (Shqipëri)",
41429         "al",
41430         "355"
41431       ],
41432       [
41433         "Algeria (‫الجزائر‬‎)",
41434         "dz",
41435         "213"
41436       ],
41437       [
41438         "American Samoa",
41439         "as",
41440         "1684"
41441       ],
41442       [
41443         "Andorra",
41444         "ad",
41445         "376"
41446       ],
41447       [
41448         "Angola",
41449         "ao",
41450         "244"
41451       ],
41452       [
41453         "Anguilla",
41454         "ai",
41455         "1264"
41456       ],
41457       [
41458         "Antigua and Barbuda",
41459         "ag",
41460         "1268"
41461       ],
41462       [
41463         "Argentina",
41464         "ar",
41465         "54"
41466       ],
41467       [
41468         "Armenia (Հայաստան)",
41469         "am",
41470         "374"
41471       ],
41472       [
41473         "Aruba",
41474         "aw",
41475         "297"
41476       ],
41477       [
41478         "Australia",
41479         "au",
41480         "61",
41481         0
41482       ],
41483       [
41484         "Austria (Österreich)",
41485         "at",
41486         "43"
41487       ],
41488       [
41489         "Azerbaijan (Azərbaycan)",
41490         "az",
41491         "994"
41492       ],
41493       [
41494         "Bahamas",
41495         "bs",
41496         "1242"
41497       ],
41498       [
41499         "Bahrain (‫البحرين‬‎)",
41500         "bh",
41501         "973"
41502       ],
41503       [
41504         "Bangladesh (বাংলাদেশ)",
41505         "bd",
41506         "880"
41507       ],
41508       [
41509         "Barbados",
41510         "bb",
41511         "1246"
41512       ],
41513       [
41514         "Belarus (Беларусь)",
41515         "by",
41516         "375"
41517       ],
41518       [
41519         "Belgium (België)",
41520         "be",
41521         "32"
41522       ],
41523       [
41524         "Belize",
41525         "bz",
41526         "501"
41527       ],
41528       [
41529         "Benin (Bénin)",
41530         "bj",
41531         "229"
41532       ],
41533       [
41534         "Bermuda",
41535         "bm",
41536         "1441"
41537       ],
41538       [
41539         "Bhutan (འབྲུག)",
41540         "bt",
41541         "975"
41542       ],
41543       [
41544         "Bolivia",
41545         "bo",
41546         "591"
41547       ],
41548       [
41549         "Bosnia and Herzegovina (Босна и Херцеговина)",
41550         "ba",
41551         "387"
41552       ],
41553       [
41554         "Botswana",
41555         "bw",
41556         "267"
41557       ],
41558       [
41559         "Brazil (Brasil)",
41560         "br",
41561         "55"
41562       ],
41563       [
41564         "British Indian Ocean Territory",
41565         "io",
41566         "246"
41567       ],
41568       [
41569         "British Virgin Islands",
41570         "vg",
41571         "1284"
41572       ],
41573       [
41574         "Brunei",
41575         "bn",
41576         "673"
41577       ],
41578       [
41579         "Bulgaria (България)",
41580         "bg",
41581         "359"
41582       ],
41583       [
41584         "Burkina Faso",
41585         "bf",
41586         "226"
41587       ],
41588       [
41589         "Burundi (Uburundi)",
41590         "bi",
41591         "257"
41592       ],
41593       [
41594         "Cambodia (កម្ពុជា)",
41595         "kh",
41596         "855"
41597       ],
41598       [
41599         "Cameroon (Cameroun)",
41600         "cm",
41601         "237"
41602       ],
41603       [
41604         "Canada",
41605         "ca",
41606         "1",
41607         1,
41608         ["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"]
41609       ],
41610       [
41611         "Cape Verde (Kabu Verdi)",
41612         "cv",
41613         "238"
41614       ],
41615       [
41616         "Caribbean Netherlands",
41617         "bq",
41618         "599",
41619         1
41620       ],
41621       [
41622         "Cayman Islands",
41623         "ky",
41624         "1345"
41625       ],
41626       [
41627         "Central African Republic (République centrafricaine)",
41628         "cf",
41629         "236"
41630       ],
41631       [
41632         "Chad (Tchad)",
41633         "td",
41634         "235"
41635       ],
41636       [
41637         "Chile",
41638         "cl",
41639         "56"
41640       ],
41641       [
41642         "China (中国)",
41643         "cn",
41644         "86"
41645       ],
41646       [
41647         "Christmas Island",
41648         "cx",
41649         "61",
41650         2
41651       ],
41652       [
41653         "Cocos (Keeling) Islands",
41654         "cc",
41655         "61",
41656         1
41657       ],
41658       [
41659         "Colombia",
41660         "co",
41661         "57"
41662       ],
41663       [
41664         "Comoros (‫جزر القمر‬‎)",
41665         "km",
41666         "269"
41667       ],
41668       [
41669         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41670         "cd",
41671         "243"
41672       ],
41673       [
41674         "Congo (Republic) (Congo-Brazzaville)",
41675         "cg",
41676         "242"
41677       ],
41678       [
41679         "Cook Islands",
41680         "ck",
41681         "682"
41682       ],
41683       [
41684         "Costa Rica",
41685         "cr",
41686         "506"
41687       ],
41688       [
41689         "Côte d’Ivoire",
41690         "ci",
41691         "225"
41692       ],
41693       [
41694         "Croatia (Hrvatska)",
41695         "hr",
41696         "385"
41697       ],
41698       [
41699         "Cuba",
41700         "cu",
41701         "53"
41702       ],
41703       [
41704         "Curaçao",
41705         "cw",
41706         "599",
41707         0
41708       ],
41709       [
41710         "Cyprus (Κύπρος)",
41711         "cy",
41712         "357"
41713       ],
41714       [
41715         "Czech Republic (Česká republika)",
41716         "cz",
41717         "420"
41718       ],
41719       [
41720         "Denmark (Danmark)",
41721         "dk",
41722         "45"
41723       ],
41724       [
41725         "Djibouti",
41726         "dj",
41727         "253"
41728       ],
41729       [
41730         "Dominica",
41731         "dm",
41732         "1767"
41733       ],
41734       [
41735         "Dominican Republic (República Dominicana)",
41736         "do",
41737         "1",
41738         2,
41739         ["809", "829", "849"]
41740       ],
41741       [
41742         "Ecuador",
41743         "ec",
41744         "593"
41745       ],
41746       [
41747         "Egypt (‫مصر‬‎)",
41748         "eg",
41749         "20"
41750       ],
41751       [
41752         "El Salvador",
41753         "sv",
41754         "503"
41755       ],
41756       [
41757         "Equatorial Guinea (Guinea Ecuatorial)",
41758         "gq",
41759         "240"
41760       ],
41761       [
41762         "Eritrea",
41763         "er",
41764         "291"
41765       ],
41766       [
41767         "Estonia (Eesti)",
41768         "ee",
41769         "372"
41770       ],
41771       [
41772         "Ethiopia",
41773         "et",
41774         "251"
41775       ],
41776       [
41777         "Falkland Islands (Islas Malvinas)",
41778         "fk",
41779         "500"
41780       ],
41781       [
41782         "Faroe Islands (Føroyar)",
41783         "fo",
41784         "298"
41785       ],
41786       [
41787         "Fiji",
41788         "fj",
41789         "679"
41790       ],
41791       [
41792         "Finland (Suomi)",
41793         "fi",
41794         "358",
41795         0
41796       ],
41797       [
41798         "France",
41799         "fr",
41800         "33"
41801       ],
41802       [
41803         "French Guiana (Guyane française)",
41804         "gf",
41805         "594"
41806       ],
41807       [
41808         "French Polynesia (Polynésie française)",
41809         "pf",
41810         "689"
41811       ],
41812       [
41813         "Gabon",
41814         "ga",
41815         "241"
41816       ],
41817       [
41818         "Gambia",
41819         "gm",
41820         "220"
41821       ],
41822       [
41823         "Georgia (საქართველო)",
41824         "ge",
41825         "995"
41826       ],
41827       [
41828         "Germany (Deutschland)",
41829         "de",
41830         "49"
41831       ],
41832       [
41833         "Ghana (Gaana)",
41834         "gh",
41835         "233"
41836       ],
41837       [
41838         "Gibraltar",
41839         "gi",
41840         "350"
41841       ],
41842       [
41843         "Greece (Ελλάδα)",
41844         "gr",
41845         "30"
41846       ],
41847       [
41848         "Greenland (Kalaallit Nunaat)",
41849         "gl",
41850         "299"
41851       ],
41852       [
41853         "Grenada",
41854         "gd",
41855         "1473"
41856       ],
41857       [
41858         "Guadeloupe",
41859         "gp",
41860         "590",
41861         0
41862       ],
41863       [
41864         "Guam",
41865         "gu",
41866         "1671"
41867       ],
41868       [
41869         "Guatemala",
41870         "gt",
41871         "502"
41872       ],
41873       [
41874         "Guernsey",
41875         "gg",
41876         "44",
41877         1
41878       ],
41879       [
41880         "Guinea (Guinée)",
41881         "gn",
41882         "224"
41883       ],
41884       [
41885         "Guinea-Bissau (Guiné Bissau)",
41886         "gw",
41887         "245"
41888       ],
41889       [
41890         "Guyana",
41891         "gy",
41892         "592"
41893       ],
41894       [
41895         "Haiti",
41896         "ht",
41897         "509"
41898       ],
41899       [
41900         "Honduras",
41901         "hn",
41902         "504"
41903       ],
41904       [
41905         "Hong Kong (香港)",
41906         "hk",
41907         "852"
41908       ],
41909       [
41910         "Hungary (Magyarország)",
41911         "hu",
41912         "36"
41913       ],
41914       [
41915         "Iceland (Ísland)",
41916         "is",
41917         "354"
41918       ],
41919       [
41920         "India (भारत)",
41921         "in",
41922         "91"
41923       ],
41924       [
41925         "Indonesia",
41926         "id",
41927         "62"
41928       ],
41929       [
41930         "Iran (‫ایران‬‎)",
41931         "ir",
41932         "98"
41933       ],
41934       [
41935         "Iraq (‫العراق‬‎)",
41936         "iq",
41937         "964"
41938       ],
41939       [
41940         "Ireland",
41941         "ie",
41942         "353"
41943       ],
41944       [
41945         "Isle of Man",
41946         "im",
41947         "44",
41948         2
41949       ],
41950       [
41951         "Israel (‫ישראל‬‎)",
41952         "il",
41953         "972"
41954       ],
41955       [
41956         "Italy (Italia)",
41957         "it",
41958         "39",
41959         0
41960       ],
41961       [
41962         "Jamaica",
41963         "jm",
41964         "1876"
41965       ],
41966       [
41967         "Japan (日本)",
41968         "jp",
41969         "81"
41970       ],
41971       [
41972         "Jersey",
41973         "je",
41974         "44",
41975         3
41976       ],
41977       [
41978         "Jordan (‫الأردن‬‎)",
41979         "jo",
41980         "962"
41981       ],
41982       [
41983         "Kazakhstan (Казахстан)",
41984         "kz",
41985         "7",
41986         1
41987       ],
41988       [
41989         "Kenya",
41990         "ke",
41991         "254"
41992       ],
41993       [
41994         "Kiribati",
41995         "ki",
41996         "686"
41997       ],
41998       [
41999         "Kosovo",
42000         "xk",
42001         "383"
42002       ],
42003       [
42004         "Kuwait (‫الكويت‬‎)",
42005         "kw",
42006         "965"
42007       ],
42008       [
42009         "Kyrgyzstan (Кыргызстан)",
42010         "kg",
42011         "996"
42012       ],
42013       [
42014         "Laos (ລາວ)",
42015         "la",
42016         "856"
42017       ],
42018       [
42019         "Latvia (Latvija)",
42020         "lv",
42021         "371"
42022       ],
42023       [
42024         "Lebanon (‫لبنان‬‎)",
42025         "lb",
42026         "961"
42027       ],
42028       [
42029         "Lesotho",
42030         "ls",
42031         "266"
42032       ],
42033       [
42034         "Liberia",
42035         "lr",
42036         "231"
42037       ],
42038       [
42039         "Libya (‫ليبيا‬‎)",
42040         "ly",
42041         "218"
42042       ],
42043       [
42044         "Liechtenstein",
42045         "li",
42046         "423"
42047       ],
42048       [
42049         "Lithuania (Lietuva)",
42050         "lt",
42051         "370"
42052       ],
42053       [
42054         "Luxembourg",
42055         "lu",
42056         "352"
42057       ],
42058       [
42059         "Macau (澳門)",
42060         "mo",
42061         "853"
42062       ],
42063       [
42064         "Macedonia (FYROM) (Македонија)",
42065         "mk",
42066         "389"
42067       ],
42068       [
42069         "Madagascar (Madagasikara)",
42070         "mg",
42071         "261"
42072       ],
42073       [
42074         "Malawi",
42075         "mw",
42076         "265"
42077       ],
42078       [
42079         "Malaysia",
42080         "my",
42081         "60"
42082       ],
42083       [
42084         "Maldives",
42085         "mv",
42086         "960"
42087       ],
42088       [
42089         "Mali",
42090         "ml",
42091         "223"
42092       ],
42093       [
42094         "Malta",
42095         "mt",
42096         "356"
42097       ],
42098       [
42099         "Marshall Islands",
42100         "mh",
42101         "692"
42102       ],
42103       [
42104         "Martinique",
42105         "mq",
42106         "596"
42107       ],
42108       [
42109         "Mauritania (‫موريتانيا‬‎)",
42110         "mr",
42111         "222"
42112       ],
42113       [
42114         "Mauritius (Moris)",
42115         "mu",
42116         "230"
42117       ],
42118       [
42119         "Mayotte",
42120         "yt",
42121         "262",
42122         1
42123       ],
42124       [
42125         "Mexico (México)",
42126         "mx",
42127         "52"
42128       ],
42129       [
42130         "Micronesia",
42131         "fm",
42132         "691"
42133       ],
42134       [
42135         "Moldova (Republica Moldova)",
42136         "md",
42137         "373"
42138       ],
42139       [
42140         "Monaco",
42141         "mc",
42142         "377"
42143       ],
42144       [
42145         "Mongolia (Монгол)",
42146         "mn",
42147         "976"
42148       ],
42149       [
42150         "Montenegro (Crna Gora)",
42151         "me",
42152         "382"
42153       ],
42154       [
42155         "Montserrat",
42156         "ms",
42157         "1664"
42158       ],
42159       [
42160         "Morocco (‫المغرب‬‎)",
42161         "ma",
42162         "212",
42163         0
42164       ],
42165       [
42166         "Mozambique (Moçambique)",
42167         "mz",
42168         "258"
42169       ],
42170       [
42171         "Myanmar (Burma) (မြန်မာ)",
42172         "mm",
42173         "95"
42174       ],
42175       [
42176         "Namibia (Namibië)",
42177         "na",
42178         "264"
42179       ],
42180       [
42181         "Nauru",
42182         "nr",
42183         "674"
42184       ],
42185       [
42186         "Nepal (नेपाल)",
42187         "np",
42188         "977"
42189       ],
42190       [
42191         "Netherlands (Nederland)",
42192         "nl",
42193         "31"
42194       ],
42195       [
42196         "New Caledonia (Nouvelle-Calédonie)",
42197         "nc",
42198         "687"
42199       ],
42200       [
42201         "New Zealand",
42202         "nz",
42203         "64"
42204       ],
42205       [
42206         "Nicaragua",
42207         "ni",
42208         "505"
42209       ],
42210       [
42211         "Niger (Nijar)",
42212         "ne",
42213         "227"
42214       ],
42215       [
42216         "Nigeria",
42217         "ng",
42218         "234"
42219       ],
42220       [
42221         "Niue",
42222         "nu",
42223         "683"
42224       ],
42225       [
42226         "Norfolk Island",
42227         "nf",
42228         "672"
42229       ],
42230       [
42231         "North Korea (조선 민주주의 인민 공화국)",
42232         "kp",
42233         "850"
42234       ],
42235       [
42236         "Northern Mariana Islands",
42237         "mp",
42238         "1670"
42239       ],
42240       [
42241         "Norway (Norge)",
42242         "no",
42243         "47",
42244         0
42245       ],
42246       [
42247         "Oman (‫عُمان‬‎)",
42248         "om",
42249         "968"
42250       ],
42251       [
42252         "Pakistan (‫پاکستان‬‎)",
42253         "pk",
42254         "92"
42255       ],
42256       [
42257         "Palau",
42258         "pw",
42259         "680"
42260       ],
42261       [
42262         "Palestine (‫فلسطين‬‎)",
42263         "ps",
42264         "970"
42265       ],
42266       [
42267         "Panama (Panamá)",
42268         "pa",
42269         "507"
42270       ],
42271       [
42272         "Papua New Guinea",
42273         "pg",
42274         "675"
42275       ],
42276       [
42277         "Paraguay",
42278         "py",
42279         "595"
42280       ],
42281       [
42282         "Peru (Perú)",
42283         "pe",
42284         "51"
42285       ],
42286       [
42287         "Philippines",
42288         "ph",
42289         "63"
42290       ],
42291       [
42292         "Poland (Polska)",
42293         "pl",
42294         "48"
42295       ],
42296       [
42297         "Portugal",
42298         "pt",
42299         "351"
42300       ],
42301       [
42302         "Puerto Rico",
42303         "pr",
42304         "1",
42305         3,
42306         ["787", "939"]
42307       ],
42308       [
42309         "Qatar (‫قطر‬‎)",
42310         "qa",
42311         "974"
42312       ],
42313       [
42314         "Réunion (La Réunion)",
42315         "re",
42316         "262",
42317         0
42318       ],
42319       [
42320         "Romania (România)",
42321         "ro",
42322         "40"
42323       ],
42324       [
42325         "Russia (Россия)",
42326         "ru",
42327         "7",
42328         0
42329       ],
42330       [
42331         "Rwanda",
42332         "rw",
42333         "250"
42334       ],
42335       [
42336         "Saint Barthélemy",
42337         "bl",
42338         "590",
42339         1
42340       ],
42341       [
42342         "Saint Helena",
42343         "sh",
42344         "290"
42345       ],
42346       [
42347         "Saint Kitts and Nevis",
42348         "kn",
42349         "1869"
42350       ],
42351       [
42352         "Saint Lucia",
42353         "lc",
42354         "1758"
42355       ],
42356       [
42357         "Saint Martin (Saint-Martin (partie française))",
42358         "mf",
42359         "590",
42360         2
42361       ],
42362       [
42363         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42364         "pm",
42365         "508"
42366       ],
42367       [
42368         "Saint Vincent and the Grenadines",
42369         "vc",
42370         "1784"
42371       ],
42372       [
42373         "Samoa",
42374         "ws",
42375         "685"
42376       ],
42377       [
42378         "San Marino",
42379         "sm",
42380         "378"
42381       ],
42382       [
42383         "São Tomé and Príncipe (São Tomé e Príncipe)",
42384         "st",
42385         "239"
42386       ],
42387       [
42388         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42389         "sa",
42390         "966"
42391       ],
42392       [
42393         "Senegal (Sénégal)",
42394         "sn",
42395         "221"
42396       ],
42397       [
42398         "Serbia (Србија)",
42399         "rs",
42400         "381"
42401       ],
42402       [
42403         "Seychelles",
42404         "sc",
42405         "248"
42406       ],
42407       [
42408         "Sierra Leone",
42409         "sl",
42410         "232"
42411       ],
42412       [
42413         "Singapore",
42414         "sg",
42415         "65"
42416       ],
42417       [
42418         "Sint Maarten",
42419         "sx",
42420         "1721"
42421       ],
42422       [
42423         "Slovakia (Slovensko)",
42424         "sk",
42425         "421"
42426       ],
42427       [
42428         "Slovenia (Slovenija)",
42429         "si",
42430         "386"
42431       ],
42432       [
42433         "Solomon Islands",
42434         "sb",
42435         "677"
42436       ],
42437       [
42438         "Somalia (Soomaaliya)",
42439         "so",
42440         "252"
42441       ],
42442       [
42443         "South Africa",
42444         "za",
42445         "27"
42446       ],
42447       [
42448         "South Korea (대한민국)",
42449         "kr",
42450         "82"
42451       ],
42452       [
42453         "South Sudan (‫جنوب السودان‬‎)",
42454         "ss",
42455         "211"
42456       ],
42457       [
42458         "Spain (España)",
42459         "es",
42460         "34"
42461       ],
42462       [
42463         "Sri Lanka (ශ්‍රී ලංකාව)",
42464         "lk",
42465         "94"
42466       ],
42467       [
42468         "Sudan (‫السودان‬‎)",
42469         "sd",
42470         "249"
42471       ],
42472       [
42473         "Suriname",
42474         "sr",
42475         "597"
42476       ],
42477       [
42478         "Svalbard and Jan Mayen",
42479         "sj",
42480         "47",
42481         1
42482       ],
42483       [
42484         "Swaziland",
42485         "sz",
42486         "268"
42487       ],
42488       [
42489         "Sweden (Sverige)",
42490         "se",
42491         "46"
42492       ],
42493       [
42494         "Switzerland (Schweiz)",
42495         "ch",
42496         "41"
42497       ],
42498       [
42499         "Syria (‫سوريا‬‎)",
42500         "sy",
42501         "963"
42502       ],
42503       [
42504         "Taiwan (台灣)",
42505         "tw",
42506         "886"
42507       ],
42508       [
42509         "Tajikistan",
42510         "tj",
42511         "992"
42512       ],
42513       [
42514         "Tanzania",
42515         "tz",
42516         "255"
42517       ],
42518       [
42519         "Thailand (ไทย)",
42520         "th",
42521         "66"
42522       ],
42523       [
42524         "Timor-Leste",
42525         "tl",
42526         "670"
42527       ],
42528       [
42529         "Togo",
42530         "tg",
42531         "228"
42532       ],
42533       [
42534         "Tokelau",
42535         "tk",
42536         "690"
42537       ],
42538       [
42539         "Tonga",
42540         "to",
42541         "676"
42542       ],
42543       [
42544         "Trinidad and Tobago",
42545         "tt",
42546         "1868"
42547       ],
42548       [
42549         "Tunisia (‫تونس‬‎)",
42550         "tn",
42551         "216"
42552       ],
42553       [
42554         "Turkey (Türkiye)",
42555         "tr",
42556         "90"
42557       ],
42558       [
42559         "Turkmenistan",
42560         "tm",
42561         "993"
42562       ],
42563       [
42564         "Turks and Caicos Islands",
42565         "tc",
42566         "1649"
42567       ],
42568       [
42569         "Tuvalu",
42570         "tv",
42571         "688"
42572       ],
42573       [
42574         "U.S. Virgin Islands",
42575         "vi",
42576         "1340"
42577       ],
42578       [
42579         "Uganda",
42580         "ug",
42581         "256"
42582       ],
42583       [
42584         "Ukraine (Україна)",
42585         "ua",
42586         "380"
42587       ],
42588       [
42589         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42590         "ae",
42591         "971"
42592       ],
42593       [
42594         "United Kingdom",
42595         "gb",
42596         "44",
42597         0
42598       ],
42599       [
42600         "United States",
42601         "us",
42602         "1",
42603         0
42604       ],
42605       [
42606         "Uruguay",
42607         "uy",
42608         "598"
42609       ],
42610       [
42611         "Uzbekistan (Oʻzbekiston)",
42612         "uz",
42613         "998"
42614       ],
42615       [
42616         "Vanuatu",
42617         "vu",
42618         "678"
42619       ],
42620       [
42621         "Vatican City (Città del Vaticano)",
42622         "va",
42623         "39",
42624         1
42625       ],
42626       [
42627         "Venezuela",
42628         "ve",
42629         "58"
42630       ],
42631       [
42632         "Vietnam (Việt Nam)",
42633         "vn",
42634         "84"
42635       ],
42636       [
42637         "Wallis and Futuna (Wallis-et-Futuna)",
42638         "wf",
42639         "681"
42640       ],
42641       [
42642         "Western Sahara (‫الصحراء الغربية‬‎)",
42643         "eh",
42644         "212",
42645         1
42646       ],
42647       [
42648         "Yemen (‫اليمن‬‎)",
42649         "ye",
42650         "967"
42651       ],
42652       [
42653         "Zambia",
42654         "zm",
42655         "260"
42656       ],
42657       [
42658         "Zimbabwe",
42659         "zw",
42660         "263"
42661       ],
42662       [
42663         "Åland Islands",
42664         "ax",
42665         "358",
42666         1
42667       ]
42668   ];
42669   
42670   return d;
42671 }/**
42672 *    This script refer to:
42673 *    Title: International Telephone Input
42674 *    Author: Jack O'Connor
42675 *    Code version:  v12.1.12
42676 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42677 **/
42678
42679 /**
42680  * @class Roo.bootstrap.PhoneInput
42681  * @extends Roo.bootstrap.TriggerField
42682  * An input with International dial-code selection
42683  
42684  * @cfg {String} defaultDialCode default '+852'
42685  * @cfg {Array} preferedCountries default []
42686   
42687  * @constructor
42688  * Create a new PhoneInput.
42689  * @param {Object} config Configuration options
42690  */
42691
42692 Roo.bootstrap.PhoneInput = function(config) {
42693     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42694 };
42695
42696 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42697         
42698         listWidth: undefined,
42699         
42700         selectedClass: 'active',
42701         
42702         invalidClass : "has-warning",
42703         
42704         validClass: 'has-success',
42705         
42706         allowed: '0123456789',
42707         
42708         max_length: 15,
42709         
42710         /**
42711          * @cfg {String} defaultDialCode The default dial code when initializing the input
42712          */
42713         defaultDialCode: '+852',
42714         
42715         /**
42716          * @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
42717          */
42718         preferedCountries: false,
42719         
42720         getAutoCreate : function()
42721         {
42722             var data = Roo.bootstrap.PhoneInputData();
42723             var align = this.labelAlign || this.parentLabelAlign();
42724             var id = Roo.id();
42725             
42726             this.allCountries = [];
42727             this.dialCodeMapping = [];
42728             
42729             for (var i = 0; i < data.length; i++) {
42730               var c = data[i];
42731               this.allCountries[i] = {
42732                 name: c[0],
42733                 iso2: c[1],
42734                 dialCode: c[2],
42735                 priority: c[3] || 0,
42736                 areaCodes: c[4] || null
42737               };
42738               this.dialCodeMapping[c[2]] = {
42739                   name: c[0],
42740                   iso2: c[1],
42741                   priority: c[3] || 0,
42742                   areaCodes: c[4] || null
42743               };
42744             }
42745             
42746             var cfg = {
42747                 cls: 'form-group',
42748                 cn: []
42749             };
42750             
42751             var input =  {
42752                 tag: 'input',
42753                 id : id,
42754                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42755                 maxlength: this.max_length,
42756                 cls : 'form-control tel-input',
42757                 autocomplete: 'new-password'
42758             };
42759             
42760             var hiddenInput = {
42761                 tag: 'input',
42762                 type: 'hidden',
42763                 cls: 'hidden-tel-input'
42764             };
42765             
42766             if (this.name) {
42767                 hiddenInput.name = this.name;
42768             }
42769             
42770             if (this.disabled) {
42771                 input.disabled = true;
42772             }
42773             
42774             var flag_container = {
42775                 tag: 'div',
42776                 cls: 'flag-box',
42777                 cn: [
42778                     {
42779                         tag: 'div',
42780                         cls: 'flag'
42781                     },
42782                     {
42783                         tag: 'div',
42784                         cls: 'caret'
42785                     }
42786                 ]
42787             };
42788             
42789             var box = {
42790                 tag: 'div',
42791                 cls: this.hasFeedback ? 'has-feedback' : '',
42792                 cn: [
42793                     hiddenInput,
42794                     input,
42795                     {
42796                         tag: 'input',
42797                         cls: 'dial-code-holder',
42798                         disabled: true
42799                     }
42800                 ]
42801             };
42802             
42803             var container = {
42804                 cls: 'roo-select2-container input-group',
42805                 cn: [
42806                     flag_container,
42807                     box
42808                 ]
42809             };
42810             
42811             if (this.fieldLabel.length) {
42812                 var indicator = {
42813                     tag: 'i',
42814                     tooltip: 'This field is required'
42815                 };
42816                 
42817                 var label = {
42818                     tag: 'label',
42819                     'for':  id,
42820                     cls: 'control-label',
42821                     cn: []
42822                 };
42823                 
42824                 var label_text = {
42825                     tag: 'span',
42826                     html: this.fieldLabel
42827                 };
42828                 
42829                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42830                 label.cn = [
42831                     indicator,
42832                     label_text
42833                 ];
42834                 
42835                 if(this.indicatorpos == 'right') {
42836                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42837                     label.cn = [
42838                         label_text,
42839                         indicator
42840                     ];
42841                 }
42842                 
42843                 if(align == 'left') {
42844                     container = {
42845                         tag: 'div',
42846                         cn: [
42847                             container
42848                         ]
42849                     };
42850                     
42851                     if(this.labelWidth > 12){
42852                         label.style = "width: " + this.labelWidth + 'px';
42853                     }
42854                     if(this.labelWidth < 13 && this.labelmd == 0){
42855                         this.labelmd = this.labelWidth;
42856                     }
42857                     if(this.labellg > 0){
42858                         label.cls += ' col-lg-' + this.labellg;
42859                         input.cls += ' col-lg-' + (12 - this.labellg);
42860                     }
42861                     if(this.labelmd > 0){
42862                         label.cls += ' col-md-' + this.labelmd;
42863                         container.cls += ' col-md-' + (12 - this.labelmd);
42864                     }
42865                     if(this.labelsm > 0){
42866                         label.cls += ' col-sm-' + this.labelsm;
42867                         container.cls += ' col-sm-' + (12 - this.labelsm);
42868                     }
42869                     if(this.labelxs > 0){
42870                         label.cls += ' col-xs-' + this.labelxs;
42871                         container.cls += ' col-xs-' + (12 - this.labelxs);
42872                     }
42873                 }
42874             }
42875             
42876             cfg.cn = [
42877                 label,
42878                 container
42879             ];
42880             
42881             var settings = this;
42882             
42883             ['xs','sm','md','lg'].map(function(size){
42884                 if (settings[size]) {
42885                     cfg.cls += ' col-' + size + '-' + settings[size];
42886                 }
42887             });
42888             
42889             this.store = new Roo.data.Store({
42890                 proxy : new Roo.data.MemoryProxy({}),
42891                 reader : new Roo.data.JsonReader({
42892                     fields : [
42893                         {
42894                             'name' : 'name',
42895                             'type' : 'string'
42896                         },
42897                         {
42898                             'name' : 'iso2',
42899                             'type' : 'string'
42900                         },
42901                         {
42902                             'name' : 'dialCode',
42903                             'type' : 'string'
42904                         },
42905                         {
42906                             'name' : 'priority',
42907                             'type' : 'string'
42908                         },
42909                         {
42910                             'name' : 'areaCodes',
42911                             'type' : 'string'
42912                         }
42913                     ]
42914                 })
42915             });
42916             
42917             if(!this.preferedCountries) {
42918                 this.preferedCountries = [
42919                     'hk',
42920                     'gb',
42921                     'us'
42922                 ];
42923             }
42924             
42925             var p = this.preferedCountries.reverse();
42926             
42927             if(p) {
42928                 for (var i = 0; i < p.length; i++) {
42929                     for (var j = 0; j < this.allCountries.length; j++) {
42930                         if(this.allCountries[j].iso2 == p[i]) {
42931                             var t = this.allCountries[j];
42932                             this.allCountries.splice(j,1);
42933                             this.allCountries.unshift(t);
42934                         }
42935                     } 
42936                 }
42937             }
42938             
42939             this.store.proxy.data = {
42940                 success: true,
42941                 data: this.allCountries
42942             };
42943             
42944             return cfg;
42945         },
42946         
42947         initEvents : function()
42948         {
42949             this.createList();
42950             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42951             
42952             this.indicator = this.indicatorEl();
42953             this.flag = this.flagEl();
42954             this.dialCodeHolder = this.dialCodeHolderEl();
42955             
42956             this.trigger = this.el.select('div.flag-box',true).first();
42957             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42958             
42959             var _this = this;
42960             
42961             (function(){
42962                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42963                 _this.list.setWidth(lw);
42964             }).defer(100);
42965             
42966             this.list.on('mouseover', this.onViewOver, this);
42967             this.list.on('mousemove', this.onViewMove, this);
42968             this.inputEl().on("keyup", this.onKeyUp, this);
42969             this.inputEl().on("keypress", this.onKeyPress, this);
42970             
42971             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42972
42973             this.view = new Roo.View(this.list, this.tpl, {
42974                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42975             });
42976             
42977             this.view.on('click', this.onViewClick, this);
42978             this.setValue(this.defaultDialCode);
42979         },
42980         
42981         onTriggerClick : function(e)
42982         {
42983             Roo.log('trigger click');
42984             if(this.disabled){
42985                 return;
42986             }
42987             
42988             if(this.isExpanded()){
42989                 this.collapse();
42990                 this.hasFocus = false;
42991             }else {
42992                 this.store.load({});
42993                 this.hasFocus = true;
42994                 this.expand();
42995             }
42996         },
42997         
42998         isExpanded : function()
42999         {
43000             return this.list.isVisible();
43001         },
43002         
43003         collapse : function()
43004         {
43005             if(!this.isExpanded()){
43006                 return;
43007             }
43008             this.list.hide();
43009             Roo.get(document).un('mousedown', this.collapseIf, this);
43010             Roo.get(document).un('mousewheel', this.collapseIf, this);
43011             this.fireEvent('collapse', this);
43012             this.validate();
43013         },
43014         
43015         expand : function()
43016         {
43017             Roo.log('expand');
43018
43019             if(this.isExpanded() || !this.hasFocus){
43020                 return;
43021             }
43022             
43023             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43024             this.list.setWidth(lw);
43025             
43026             this.list.show();
43027             this.restrictHeight();
43028             
43029             Roo.get(document).on('mousedown', this.collapseIf, this);
43030             Roo.get(document).on('mousewheel', this.collapseIf, this);
43031             
43032             this.fireEvent('expand', this);
43033         },
43034         
43035         restrictHeight : function()
43036         {
43037             this.list.alignTo(this.inputEl(), this.listAlign);
43038             this.list.alignTo(this.inputEl(), this.listAlign);
43039         },
43040         
43041         onViewOver : function(e, t)
43042         {
43043             if(this.inKeyMode){
43044                 return;
43045             }
43046             var item = this.view.findItemFromChild(t);
43047             
43048             if(item){
43049                 var index = this.view.indexOf(item);
43050                 this.select(index, false);
43051             }
43052         },
43053
43054         // private
43055         onViewClick : function(view, doFocus, el, e)
43056         {
43057             var index = this.view.getSelectedIndexes()[0];
43058             
43059             var r = this.store.getAt(index);
43060             
43061             if(r){
43062                 this.onSelect(r, index);
43063             }
43064             if(doFocus !== false && !this.blockFocus){
43065                 this.inputEl().focus();
43066             }
43067         },
43068         
43069         onViewMove : function(e, t)
43070         {
43071             this.inKeyMode = false;
43072         },
43073         
43074         select : function(index, scrollIntoView)
43075         {
43076             this.selectedIndex = index;
43077             this.view.select(index);
43078             if(scrollIntoView !== false){
43079                 var el = this.view.getNode(index);
43080                 if(el){
43081                     this.list.scrollChildIntoView(el, false);
43082                 }
43083             }
43084         },
43085         
43086         createList : function()
43087         {
43088             this.list = Roo.get(document.body).createChild({
43089                 tag: 'ul',
43090                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43091                 style: 'display:none'
43092             });
43093             
43094             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43095         },
43096         
43097         collapseIf : function(e)
43098         {
43099             var in_combo  = e.within(this.el);
43100             var in_list =  e.within(this.list);
43101             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43102             
43103             if (in_combo || in_list || is_list) {
43104                 return;
43105             }
43106             this.collapse();
43107         },
43108         
43109         onSelect : function(record, index)
43110         {
43111             if(this.fireEvent('beforeselect', this, record, index) !== false){
43112                 
43113                 this.setFlagClass(record.data.iso2);
43114                 this.setDialCode(record.data.dialCode);
43115                 this.hasFocus = false;
43116                 this.collapse();
43117                 this.fireEvent('select', this, record, index);
43118             }
43119         },
43120         
43121         flagEl : function()
43122         {
43123             var flag = this.el.select('div.flag',true).first();
43124             if(!flag){
43125                 return false;
43126             }
43127             return flag;
43128         },
43129         
43130         dialCodeHolderEl : function()
43131         {
43132             var d = this.el.select('input.dial-code-holder',true).first();
43133             if(!d){
43134                 return false;
43135             }
43136             return d;
43137         },
43138         
43139         setDialCode : function(v)
43140         {
43141             this.dialCodeHolder.dom.value = '+'+v;
43142         },
43143         
43144         setFlagClass : function(n)
43145         {
43146             this.flag.dom.className = 'flag '+n;
43147         },
43148         
43149         getValue : function()
43150         {
43151             var v = this.inputEl().getValue();
43152             if(this.dialCodeHolder) {
43153                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43154             }
43155             return v;
43156         },
43157         
43158         setValue : function(v)
43159         {
43160             var d = this.getDialCode(v);
43161             
43162             //invalid dial code
43163             if(v.length == 0 || !d || d.length == 0) {
43164                 if(this.rendered){
43165                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43166                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43167                 }
43168                 return;
43169             }
43170             
43171             //valid dial code
43172             this.setFlagClass(this.dialCodeMapping[d].iso2);
43173             this.setDialCode(d);
43174             this.inputEl().dom.value = v.replace('+'+d,'');
43175             this.hiddenEl().dom.value = this.getValue();
43176             
43177             this.validate();
43178         },
43179         
43180         getDialCode : function(v)
43181         {
43182             v = v ||  '';
43183             
43184             if (v.length == 0) {
43185                 return this.dialCodeHolder.dom.value;
43186             }
43187             
43188             var dialCode = "";
43189             if (v.charAt(0) != "+") {
43190                 return false;
43191             }
43192             var numericChars = "";
43193             for (var i = 1; i < v.length; i++) {
43194               var c = v.charAt(i);
43195               if (!isNaN(c)) {
43196                 numericChars += c;
43197                 if (this.dialCodeMapping[numericChars]) {
43198                   dialCode = v.substr(1, i);
43199                 }
43200                 if (numericChars.length == 4) {
43201                   break;
43202                 }
43203               }
43204             }
43205             return dialCode;
43206         },
43207         
43208         reset : function()
43209         {
43210             this.setValue(this.defaultDialCode);
43211             this.validate();
43212         },
43213         
43214         hiddenEl : function()
43215         {
43216             return this.el.select('input.hidden-tel-input',true).first();
43217         },
43218         
43219         // after setting val
43220         onKeyUp : function(e){
43221             this.setValue(this.getValue());
43222         },
43223         
43224         onKeyPress : function(e){
43225             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43226                 e.stopEvent();
43227             }
43228         }
43229         
43230 });
43231 /**
43232  * @class Roo.bootstrap.MoneyField
43233  * @extends Roo.bootstrap.ComboBox
43234  * Bootstrap MoneyField class
43235  * 
43236  * @constructor
43237  * Create a new MoneyField.
43238  * @param {Object} config Configuration options
43239  */
43240
43241 Roo.bootstrap.MoneyField = function(config) {
43242     
43243     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43244     
43245 };
43246
43247 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43248     
43249     /**
43250      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43251      */
43252     allowDecimals : true,
43253     /**
43254      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43255      */
43256     decimalSeparator : ".",
43257     /**
43258      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43259      */
43260     decimalPrecision : 0,
43261     /**
43262      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43263      */
43264     allowNegative : true,
43265     /**
43266      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43267      */
43268     allowZero: true,
43269     /**
43270      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43271      */
43272     minValue : Number.NEGATIVE_INFINITY,
43273     /**
43274      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43275      */
43276     maxValue : Number.MAX_VALUE,
43277     /**
43278      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43279      */
43280     minText : "The minimum value for this field is {0}",
43281     /**
43282      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43283      */
43284     maxText : "The maximum value for this field is {0}",
43285     /**
43286      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43287      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43288      */
43289     nanText : "{0} is not a valid number",
43290     /**
43291      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43292      */
43293     castInt : true,
43294     /**
43295      * @cfg {String} defaults currency of the MoneyField
43296      * value should be in lkey
43297      */
43298     defaultCurrency : false,
43299     /**
43300      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43301      */
43302     thousandsDelimiter : false,
43303     /**
43304      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43305      */
43306     max_length: false,
43307     
43308     inputlg : 9,
43309     inputmd : 9,
43310     inputsm : 9,
43311     inputxs : 6,
43312     
43313     store : false,
43314     
43315     getAutoCreate : function()
43316     {
43317         var align = this.labelAlign || this.parentLabelAlign();
43318         
43319         var id = Roo.id();
43320
43321         var cfg = {
43322             cls: 'form-group',
43323             cn: []
43324         };
43325
43326         var input =  {
43327             tag: 'input',
43328             id : id,
43329             cls : 'form-control roo-money-amount-input',
43330             autocomplete: 'new-password'
43331         };
43332         
43333         var hiddenInput = {
43334             tag: 'input',
43335             type: 'hidden',
43336             id: Roo.id(),
43337             cls: 'hidden-number-input'
43338         };
43339         
43340         if(this.max_length) {
43341             input.maxlength = this.max_length; 
43342         }
43343         
43344         if (this.name) {
43345             hiddenInput.name = this.name;
43346         }
43347
43348         if (this.disabled) {
43349             input.disabled = true;
43350         }
43351
43352         var clg = 12 - this.inputlg;
43353         var cmd = 12 - this.inputmd;
43354         var csm = 12 - this.inputsm;
43355         var cxs = 12 - this.inputxs;
43356         
43357         var container = {
43358             tag : 'div',
43359             cls : 'row roo-money-field',
43360             cn : [
43361                 {
43362                     tag : 'div',
43363                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43364                     cn : [
43365                         {
43366                             tag : 'div',
43367                             cls: 'roo-select2-container input-group',
43368                             cn: [
43369                                 {
43370                                     tag : 'input',
43371                                     cls : 'form-control roo-money-currency-input',
43372                                     autocomplete: 'new-password',
43373                                     readOnly : 1,
43374                                     name : this.currencyName
43375                                 },
43376                                 {
43377                                     tag :'span',
43378                                     cls : 'input-group-addon',
43379                                     cn : [
43380                                         {
43381                                             tag: 'span',
43382                                             cls: 'caret'
43383                                         }
43384                                     ]
43385                                 }
43386                             ]
43387                         }
43388                     ]
43389                 },
43390                 {
43391                     tag : 'div',
43392                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43393                     cn : [
43394                         {
43395                             tag: 'div',
43396                             cls: this.hasFeedback ? 'has-feedback' : '',
43397                             cn: [
43398                                 input
43399                             ]
43400                         }
43401                     ]
43402                 }
43403             ]
43404             
43405         };
43406         
43407         if (this.fieldLabel.length) {
43408             var indicator = {
43409                 tag: 'i',
43410                 tooltip: 'This field is required'
43411             };
43412
43413             var label = {
43414                 tag: 'label',
43415                 'for':  id,
43416                 cls: 'control-label',
43417                 cn: []
43418             };
43419
43420             var label_text = {
43421                 tag: 'span',
43422                 html: this.fieldLabel
43423             };
43424
43425             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43426             label.cn = [
43427                 indicator,
43428                 label_text
43429             ];
43430
43431             if(this.indicatorpos == 'right') {
43432                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43433                 label.cn = [
43434                     label_text,
43435                     indicator
43436                 ];
43437             }
43438
43439             if(align == 'left') {
43440                 container = {
43441                     tag: 'div',
43442                     cn: [
43443                         container
43444                     ]
43445                 };
43446
43447                 if(this.labelWidth > 12){
43448                     label.style = "width: " + this.labelWidth + 'px';
43449                 }
43450                 if(this.labelWidth < 13 && this.labelmd == 0){
43451                     this.labelmd = this.labelWidth;
43452                 }
43453                 if(this.labellg > 0){
43454                     label.cls += ' col-lg-' + this.labellg;
43455                     input.cls += ' col-lg-' + (12 - this.labellg);
43456                 }
43457                 if(this.labelmd > 0){
43458                     label.cls += ' col-md-' + this.labelmd;
43459                     container.cls += ' col-md-' + (12 - this.labelmd);
43460                 }
43461                 if(this.labelsm > 0){
43462                     label.cls += ' col-sm-' + this.labelsm;
43463                     container.cls += ' col-sm-' + (12 - this.labelsm);
43464                 }
43465                 if(this.labelxs > 0){
43466                     label.cls += ' col-xs-' + this.labelxs;
43467                     container.cls += ' col-xs-' + (12 - this.labelxs);
43468                 }
43469             }
43470         }
43471
43472         cfg.cn = [
43473             label,
43474             container,
43475             hiddenInput
43476         ];
43477         
43478         var settings = this;
43479
43480         ['xs','sm','md','lg'].map(function(size){
43481             if (settings[size]) {
43482                 cfg.cls += ' col-' + size + '-' + settings[size];
43483             }
43484         });
43485         
43486         return cfg;
43487     },
43488     
43489     initEvents : function()
43490     {
43491         this.indicator = this.indicatorEl();
43492         
43493         this.initCurrencyEvent();
43494         
43495         this.initNumberEvent();
43496     },
43497     
43498     initCurrencyEvent : function()
43499     {
43500         if (!this.store) {
43501             throw "can not find store for combo";
43502         }
43503         
43504         this.store = Roo.factory(this.store, Roo.data);
43505         this.store.parent = this;
43506         
43507         this.createList();
43508         
43509         this.triggerEl = this.el.select('.input-group-addon', true).first();
43510         
43511         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43512         
43513         var _this = this;
43514         
43515         (function(){
43516             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43517             _this.list.setWidth(lw);
43518         }).defer(100);
43519         
43520         this.list.on('mouseover', this.onViewOver, this);
43521         this.list.on('mousemove', this.onViewMove, this);
43522         this.list.on('scroll', this.onViewScroll, this);
43523         
43524         if(!this.tpl){
43525             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43526         }
43527         
43528         this.view = new Roo.View(this.list, this.tpl, {
43529             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43530         });
43531         
43532         this.view.on('click', this.onViewClick, this);
43533         
43534         this.store.on('beforeload', this.onBeforeLoad, this);
43535         this.store.on('load', this.onLoad, this);
43536         this.store.on('loadexception', this.onLoadException, this);
43537         
43538         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43539             "up" : function(e){
43540                 this.inKeyMode = true;
43541                 this.selectPrev();
43542             },
43543
43544             "down" : function(e){
43545                 if(!this.isExpanded()){
43546                     this.onTriggerClick();
43547                 }else{
43548                     this.inKeyMode = true;
43549                     this.selectNext();
43550                 }
43551             },
43552
43553             "enter" : function(e){
43554                 this.collapse();
43555                 
43556                 if(this.fireEvent("specialkey", this, e)){
43557                     this.onViewClick(false);
43558                 }
43559                 
43560                 return true;
43561             },
43562
43563             "esc" : function(e){
43564                 this.collapse();
43565             },
43566
43567             "tab" : function(e){
43568                 this.collapse();
43569                 
43570                 if(this.fireEvent("specialkey", this, e)){
43571                     this.onViewClick(false);
43572                 }
43573                 
43574                 return true;
43575             },
43576
43577             scope : this,
43578
43579             doRelay : function(foo, bar, hname){
43580                 if(hname == 'down' || this.scope.isExpanded()){
43581                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43582                 }
43583                 return true;
43584             },
43585
43586             forceKeyDown: true
43587         });
43588         
43589         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43590         
43591     },
43592     
43593     initNumberEvent : function(e)
43594     {
43595         this.inputEl().on("keydown" , this.fireKey,  this);
43596         this.inputEl().on("focus", this.onFocus,  this);
43597         this.inputEl().on("blur", this.onBlur,  this);
43598         
43599         this.inputEl().relayEvent('keyup', this);
43600         
43601         if(this.indicator){
43602             this.indicator.addClass('invisible');
43603         }
43604  
43605         this.originalValue = this.getValue();
43606         
43607         if(this.validationEvent == 'keyup'){
43608             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43609             this.inputEl().on('keyup', this.filterValidation, this);
43610         }
43611         else if(this.validationEvent !== false){
43612             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43613         }
43614         
43615         if(this.selectOnFocus){
43616             this.on("focus", this.preFocus, this);
43617             
43618         }
43619         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43620             this.inputEl().on("keypress", this.filterKeys, this);
43621         } else {
43622             this.inputEl().relayEvent('keypress', this);
43623         }
43624         
43625         var allowed = "0123456789";
43626         
43627         if(this.allowDecimals){
43628             allowed += this.decimalSeparator;
43629         }
43630         
43631         if(this.allowNegative){
43632             allowed += "-";
43633         }
43634         
43635         if(this.thousandsDelimiter) {
43636             allowed += ",";
43637         }
43638         
43639         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43640         
43641         var keyPress = function(e){
43642             
43643             var k = e.getKey();
43644             
43645             var c = e.getCharCode();
43646             
43647             if(
43648                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43649                     allowed.indexOf(String.fromCharCode(c)) === -1
43650             ){
43651                 e.stopEvent();
43652                 return;
43653             }
43654             
43655             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43656                 return;
43657             }
43658             
43659             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43660                 e.stopEvent();
43661             }
43662         };
43663         
43664         this.inputEl().on("keypress", keyPress, this);
43665         
43666     },
43667     
43668     onTriggerClick : function(e)
43669     {   
43670         if(this.disabled){
43671             return;
43672         }
43673         
43674         this.page = 0;
43675         this.loadNext = false;
43676         
43677         if(this.isExpanded()){
43678             this.collapse();
43679             return;
43680         }
43681         
43682         this.hasFocus = true;
43683         
43684         if(this.triggerAction == 'all') {
43685             this.doQuery(this.allQuery, true);
43686             return;
43687         }
43688         
43689         this.doQuery(this.getRawValue());
43690     },
43691     
43692     getCurrency : function()
43693     {   
43694         var v = this.currencyEl().getValue();
43695         
43696         return v;
43697     },
43698     
43699     restrictHeight : function()
43700     {
43701         this.list.alignTo(this.currencyEl(), this.listAlign);
43702         this.list.alignTo(this.currencyEl(), this.listAlign);
43703     },
43704     
43705     onViewClick : function(view, doFocus, el, e)
43706     {
43707         var index = this.view.getSelectedIndexes()[0];
43708         
43709         var r = this.store.getAt(index);
43710         
43711         if(r){
43712             this.onSelect(r, index);
43713         }
43714     },
43715     
43716     onSelect : function(record, index){
43717         
43718         if(this.fireEvent('beforeselect', this, record, index) !== false){
43719         
43720             this.setFromCurrencyData(index > -1 ? record.data : false);
43721             
43722             this.collapse();
43723             
43724             this.fireEvent('select', this, record, index);
43725         }
43726     },
43727     
43728     setFromCurrencyData : function(o)
43729     {
43730         var currency = '';
43731         
43732         this.lastCurrency = o;
43733         
43734         if (this.currencyField) {
43735             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43736         } else {
43737             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43738         }
43739         
43740         this.lastSelectionText = currency;
43741         
43742         //setting default currency
43743         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43744             this.setCurrency(this.defaultCurrency);
43745             return;
43746         }
43747         
43748         this.setCurrency(currency);
43749     },
43750     
43751     setFromData : function(o)
43752     {
43753         var c = {};
43754         
43755         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43756         
43757         this.setFromCurrencyData(c);
43758         
43759         var value = '';
43760         
43761         if (this.name) {
43762             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43763         } else {
43764             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43765         }
43766         
43767         this.setValue(value);
43768         
43769     },
43770     
43771     setCurrency : function(v)
43772     {   
43773         this.currencyValue = v;
43774         
43775         if(this.rendered){
43776             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43777             this.validate();
43778         }
43779     },
43780     
43781     setValue : function(v)
43782     {
43783         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43784         
43785         this.value = v;
43786         
43787         if(this.rendered){
43788             
43789             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43790             
43791             this.inputEl().dom.value = (v == '') ? '' :
43792                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43793             
43794             if(!this.allowZero && v === '0') {
43795                 this.hiddenEl().dom.value = '';
43796                 this.inputEl().dom.value = '';
43797             }
43798             
43799             this.validate();
43800         }
43801     },
43802     
43803     getRawValue : function()
43804     {
43805         var v = this.inputEl().getValue();
43806         
43807         return v;
43808     },
43809     
43810     getValue : function()
43811     {
43812         return this.fixPrecision(this.parseValue(this.getRawValue()));
43813     },
43814     
43815     parseValue : function(value)
43816     {
43817         if(this.thousandsDelimiter) {
43818             value += "";
43819             r = new RegExp(",", "g");
43820             value = value.replace(r, "");
43821         }
43822         
43823         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43824         return isNaN(value) ? '' : value;
43825         
43826     },
43827     
43828     fixPrecision : function(value)
43829     {
43830         if(this.thousandsDelimiter) {
43831             value += "";
43832             r = new RegExp(",", "g");
43833             value = value.replace(r, "");
43834         }
43835         
43836         var nan = isNaN(value);
43837         
43838         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43839             return nan ? '' : value;
43840         }
43841         return parseFloat(value).toFixed(this.decimalPrecision);
43842     },
43843     
43844     decimalPrecisionFcn : function(v)
43845     {
43846         return Math.floor(v);
43847     },
43848     
43849     validateValue : function(value)
43850     {
43851         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43852             return false;
43853         }
43854         
43855         var num = this.parseValue(value);
43856         
43857         if(isNaN(num)){
43858             this.markInvalid(String.format(this.nanText, value));
43859             return false;
43860         }
43861         
43862         if(num < this.minValue){
43863             this.markInvalid(String.format(this.minText, this.minValue));
43864             return false;
43865         }
43866         
43867         if(num > this.maxValue){
43868             this.markInvalid(String.format(this.maxText, this.maxValue));
43869             return false;
43870         }
43871         
43872         return true;
43873     },
43874     
43875     validate : function()
43876     {
43877         if(this.disabled || this.allowBlank){
43878             this.markValid();
43879             return true;
43880         }
43881         
43882         var currency = this.getCurrency();
43883         
43884         if(this.validateValue(this.getRawValue()) && currency.length){
43885             this.markValid();
43886             return true;
43887         }
43888         
43889         this.markInvalid();
43890         return false;
43891     },
43892     
43893     getName: function()
43894     {
43895         return this.name;
43896     },
43897     
43898     beforeBlur : function()
43899     {
43900         if(!this.castInt){
43901             return;
43902         }
43903         
43904         var v = this.parseValue(this.getRawValue());
43905         
43906         if(v || v == 0){
43907             this.setValue(v);
43908         }
43909     },
43910     
43911     onBlur : function()
43912     {
43913         this.beforeBlur();
43914         
43915         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43916             //this.el.removeClass(this.focusClass);
43917         }
43918         
43919         this.hasFocus = false;
43920         
43921         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43922             this.validate();
43923         }
43924         
43925         var v = this.getValue();
43926         
43927         if(String(v) !== String(this.startValue)){
43928             this.fireEvent('change', this, v, this.startValue);
43929         }
43930         
43931         this.fireEvent("blur", this);
43932     },
43933     
43934     inputEl : function()
43935     {
43936         return this.el.select('.roo-money-amount-input', true).first();
43937     },
43938     
43939     currencyEl : function()
43940     {
43941         return this.el.select('.roo-money-currency-input', true).first();
43942     },
43943     
43944     hiddenEl : function()
43945     {
43946         return this.el.select('input.hidden-number-input',true).first();
43947     }
43948     
43949 });/**
43950  * @class Roo.bootstrap.BezierSignature
43951  * @extends Roo.bootstrap.Component
43952  * Bootstrap BezierSignature class
43953  * This script refer to:
43954  *    Title: Signature Pad
43955  *    Author: szimek
43956  *    Availability: https://github.com/szimek/signature_pad
43957  *
43958  * @constructor
43959  * Create a new BezierSignature
43960  * @param {Object} config The config object
43961  */
43962
43963 Roo.bootstrap.BezierSignature = function(config){
43964     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43965     this.addEvents({
43966         "resize" : true
43967     });
43968 };
43969
43970 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43971 {
43972      
43973     curve_data: [],
43974     
43975     is_empty: true,
43976     
43977     mouse_btn_down: true,
43978     
43979     /**
43980      * @cfg {int} canvas height
43981      */
43982     canvas_height: '200px',
43983     
43984     /**
43985      * @cfg {float|function} Radius of a single dot.
43986      */ 
43987     dot_size: false,
43988     
43989     /**
43990      * @cfg {float} Minimum width of a line. Defaults to 0.5.
43991      */
43992     min_width: 0.5,
43993     
43994     /**
43995      * @cfg {float} Maximum width of a line. Defaults to 2.5.
43996      */
43997     max_width: 2.5,
43998     
43999     /**
44000      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44001      */
44002     throttle: 16,
44003     
44004     /**
44005      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44006      */
44007     min_distance: 5,
44008     
44009     /**
44010      * @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.
44011      */
44012     bg_color: 'rgba(0, 0, 0, 0)',
44013     
44014     /**
44015      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44016      */
44017     dot_color: 'black',
44018     
44019     /**
44020      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44021      */ 
44022     velocity_filter_weight: 0.7,
44023     
44024     /**
44025      * @cfg {function} Callback when stroke begin. 
44026      */
44027     onBegin: false,
44028     
44029     /**
44030      * @cfg {function} Callback when stroke end.
44031      */
44032     onEnd: false,
44033     
44034     getAutoCreate : function()
44035     {
44036         var cls = 'roo-signature column';
44037         
44038         if(this.cls){
44039             cls += ' ' + this.cls;
44040         }
44041         
44042         var col_sizes = [
44043             'lg',
44044             'md',
44045             'sm',
44046             'xs'
44047         ];
44048         
44049         for(var i = 0; i < col_sizes.length; i++) {
44050             if(this[col_sizes[i]]) {
44051                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44052             }
44053         }
44054         
44055         var cfg = {
44056             tag: 'div',
44057             cls: cls,
44058             cn: [
44059                 {
44060                     tag: 'div',
44061                     cls: 'roo-signature-body',
44062                     cn: [
44063                         {
44064                             tag: 'canvas',
44065                             cls: 'roo-signature-body-canvas',
44066                             height: this.canvas_height,
44067                             width: this.canvas_width
44068                         }
44069                     ]
44070                 },
44071                 {
44072                     tag: 'input',
44073                     type: 'file',
44074                     style: 'display: none'
44075                 }
44076             ]
44077         };
44078         
44079         return cfg;
44080     },
44081     
44082     initEvents: function() 
44083     {
44084         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44085         
44086         var canvas = this.canvasEl();
44087         
44088         // mouse && touch event swapping...
44089         canvas.dom.style.touchAction = 'none';
44090         canvas.dom.style.msTouchAction = 'none';
44091         
44092         this.mouse_btn_down = false;
44093         canvas.on('mousedown', this._handleMouseDown, this);
44094         canvas.on('mousemove', this._handleMouseMove, this);
44095         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44096         
44097         if (window.PointerEvent) {
44098             canvas.on('pointerdown', this._handleMouseDown, this);
44099             canvas.on('pointermove', this._handleMouseMove, this);
44100             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44101         }
44102         
44103         if ('ontouchstart' in window) {
44104             canvas.on('touchstart', this._handleTouchStart, this);
44105             canvas.on('touchmove', this._handleTouchMove, this);
44106             canvas.on('touchend', this._handleTouchEnd, this);
44107         }
44108         
44109         Roo.EventManager.onWindowResize(this.resize, this, true);
44110         
44111         // file input event
44112         this.fileEl().on('change', this.uploadImage, this);
44113         
44114         this.clear();
44115         
44116         this.resize();
44117     },
44118     
44119     resize: function(){
44120         
44121         var canvas = this.canvasEl().dom;
44122         var ctx = this.canvasElCtx();
44123         var img_data = false;
44124         
44125         if(canvas.width > 0) {
44126             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44127         }
44128         // setting canvas width will clean img data
44129         canvas.width = 0;
44130         
44131         var style = window.getComputedStyle ? 
44132             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44133             
44134         var padding_left = parseInt(style.paddingLeft) || 0;
44135         var padding_right = parseInt(style.paddingRight) || 0;
44136         
44137         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44138         
44139         if(img_data) {
44140             ctx.putImageData(img_data, 0, 0);
44141         }
44142     },
44143     
44144     _handleMouseDown: function(e)
44145     {
44146         if (e.browserEvent.which === 1) {
44147             this.mouse_btn_down = true;
44148             this.strokeBegin(e);
44149         }
44150     },
44151     
44152     _handleMouseMove: function (e)
44153     {
44154         if (this.mouse_btn_down) {
44155             this.strokeMoveUpdate(e);
44156         }
44157     },
44158     
44159     _handleMouseUp: function (e)
44160     {
44161         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44162             this.mouse_btn_down = false;
44163             this.strokeEnd(e);
44164         }
44165     },
44166     
44167     _handleTouchStart: function (e) {
44168         
44169         e.preventDefault();
44170         if (e.browserEvent.targetTouches.length === 1) {
44171             // var touch = e.browserEvent.changedTouches[0];
44172             // this.strokeBegin(touch);
44173             
44174              this.strokeBegin(e); // assume e catching the correct xy...
44175         }
44176     },
44177     
44178     _handleTouchMove: function (e) {
44179         e.preventDefault();
44180         // var touch = event.targetTouches[0];
44181         // _this._strokeMoveUpdate(touch);
44182         this.strokeMoveUpdate(e);
44183     },
44184     
44185     _handleTouchEnd: function (e) {
44186         var wasCanvasTouched = e.target === this.canvasEl().dom;
44187         if (wasCanvasTouched) {
44188             e.preventDefault();
44189             // var touch = event.changedTouches[0];
44190             // _this._strokeEnd(touch);
44191             this.strokeEnd(e);
44192         }
44193     },
44194     
44195     reset: function () {
44196         this._lastPoints = [];
44197         this._lastVelocity = 0;
44198         this._lastWidth = (this.min_width + this.max_width) / 2;
44199         this.canvasElCtx().fillStyle = this.dot_color;
44200     },
44201     
44202     strokeMoveUpdate: function(e)
44203     {
44204         this.strokeUpdate(e);
44205         
44206         if (this.throttle) {
44207             this.throttleStroke(this.strokeUpdate, this.throttle);
44208         }
44209         else {
44210             this.strokeUpdate(e);
44211         }
44212     },
44213     
44214     strokeBegin: function(e)
44215     {
44216         var newPointGroup = {
44217             color: this.dot_color,
44218             points: []
44219         };
44220         
44221         if (typeof this.onBegin === 'function') {
44222             this.onBegin(e);
44223         }
44224         
44225         this.curve_data.push(newPointGroup);
44226         this.reset();
44227         this.strokeUpdate(e);
44228     },
44229     
44230     strokeUpdate: function(e)
44231     {
44232         var rect = this.canvasEl().dom.getBoundingClientRect();
44233         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44234         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44235         var lastPoints = lastPointGroup.points;
44236         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44237         var isLastPointTooClose = lastPoint
44238             ? point.distanceTo(lastPoint) <= this.min_distance
44239             : false;
44240         var color = lastPointGroup.color;
44241         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44242             var curve = this.addPoint(point);
44243             if (!lastPoint) {
44244                 this.drawDot({color: color, point: point});
44245             }
44246             else if (curve) {
44247                 this.drawCurve({color: color, curve: curve});
44248             }
44249             lastPoints.push({
44250                 time: point.time,
44251                 x: point.x,
44252                 y: point.y
44253             });
44254         }
44255     },
44256     
44257     strokeEnd: function(e)
44258     {
44259         this.strokeUpdate(e);
44260         if (typeof this.onEnd === 'function') {
44261             this.onEnd(e);
44262         }
44263     },
44264     
44265     addPoint:  function (point) {
44266         var _lastPoints = this._lastPoints;
44267         _lastPoints.push(point);
44268         if (_lastPoints.length > 2) {
44269             if (_lastPoints.length === 3) {
44270                 _lastPoints.unshift(_lastPoints[0]);
44271             }
44272             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44273             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44274             _lastPoints.shift();
44275             return curve;
44276         }
44277         return null;
44278     },
44279     
44280     calculateCurveWidths: function (startPoint, endPoint) {
44281         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44282             (1 - this.velocity_filter_weight) * this._lastVelocity;
44283
44284         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44285         var widths = {
44286             end: newWidth,
44287             start: this._lastWidth
44288         };
44289         
44290         this._lastVelocity = velocity;
44291         this._lastWidth = newWidth;
44292         return widths;
44293     },
44294     
44295     drawDot: function (_a) {
44296         var color = _a.color, point = _a.point;
44297         var ctx = this.canvasElCtx();
44298         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44299         ctx.beginPath();
44300         this.drawCurveSegment(point.x, point.y, width);
44301         ctx.closePath();
44302         ctx.fillStyle = color;
44303         ctx.fill();
44304     },
44305     
44306     drawCurve: function (_a) {
44307         var color = _a.color, curve = _a.curve;
44308         var ctx = this.canvasElCtx();
44309         var widthDelta = curve.endWidth - curve.startWidth;
44310         var drawSteps = Math.floor(curve.length()) * 2;
44311         ctx.beginPath();
44312         ctx.fillStyle = color;
44313         for (var i = 0; i < drawSteps; i += 1) {
44314         var t = i / drawSteps;
44315         var tt = t * t;
44316         var ttt = tt * t;
44317         var u = 1 - t;
44318         var uu = u * u;
44319         var uuu = uu * u;
44320         var x = uuu * curve.startPoint.x;
44321         x += 3 * uu * t * curve.control1.x;
44322         x += 3 * u * tt * curve.control2.x;
44323         x += ttt * curve.endPoint.x;
44324         var y = uuu * curve.startPoint.y;
44325         y += 3 * uu * t * curve.control1.y;
44326         y += 3 * u * tt * curve.control2.y;
44327         y += ttt * curve.endPoint.y;
44328         var width = curve.startWidth + ttt * widthDelta;
44329         this.drawCurveSegment(x, y, width);
44330         }
44331         ctx.closePath();
44332         ctx.fill();
44333     },
44334     
44335     drawCurveSegment: function (x, y, width) {
44336         var ctx = this.canvasElCtx();
44337         ctx.moveTo(x, y);
44338         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44339         this.is_empty = false;
44340     },
44341     
44342     clear: function()
44343     {
44344         var ctx = this.canvasElCtx();
44345         var canvas = this.canvasEl().dom;
44346         ctx.fillStyle = this.bg_color;
44347         ctx.clearRect(0, 0, canvas.width, canvas.height);
44348         ctx.fillRect(0, 0, canvas.width, canvas.height);
44349         this.curve_data = [];
44350         this.reset();
44351         this.is_empty = true;
44352     },
44353     
44354     fileEl: function()
44355     {
44356         return  this.el.select('input',true).first();
44357     },
44358     
44359     canvasEl: function()
44360     {
44361         return this.el.select('canvas',true).first();
44362     },
44363     
44364     canvasElCtx: function()
44365     {
44366         return this.el.select('canvas',true).first().dom.getContext('2d');
44367     },
44368     
44369     getImage: function(type)
44370     {
44371         if(this.is_empty) {
44372             return false;
44373         }
44374         
44375         // encryption ?
44376         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44377     },
44378     
44379     drawFromImage: function(img_src)
44380     {
44381         var img = new Image();
44382         
44383         img.onload = function(){
44384             this.canvasElCtx().drawImage(img, 0, 0);
44385         }.bind(this);
44386         
44387         img.src = img_src;
44388         
44389         this.is_empty = false;
44390     },
44391     
44392     selectImage: function()
44393     {
44394         this.fileEl().dom.click();
44395     },
44396     
44397     uploadImage: function(e)
44398     {
44399         var reader = new FileReader();
44400         
44401         reader.onload = function(e){
44402             var img = new Image();
44403             img.onload = function(){
44404                 this.reset();
44405                 this.canvasElCtx().drawImage(img, 0, 0);
44406             }.bind(this);
44407             img.src = e.target.result;
44408         }.bind(this);
44409         
44410         reader.readAsDataURL(e.target.files[0]);
44411     },
44412     
44413     // Bezier Point Constructor
44414     Point: (function () {
44415         function Point(x, y, time) {
44416             this.x = x;
44417             this.y = y;
44418             this.time = time || Date.now();
44419         }
44420         Point.prototype.distanceTo = function (start) {
44421             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44422         };
44423         Point.prototype.equals = function (other) {
44424             return this.x === other.x && this.y === other.y && this.time === other.time;
44425         };
44426         Point.prototype.velocityFrom = function (start) {
44427             return this.time !== start.time
44428             ? this.distanceTo(start) / (this.time - start.time)
44429             : 0;
44430         };
44431         return Point;
44432     }()),
44433     
44434     
44435     // Bezier Constructor
44436     Bezier: (function () {
44437         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44438             this.startPoint = startPoint;
44439             this.control2 = control2;
44440             this.control1 = control1;
44441             this.endPoint = endPoint;
44442             this.startWidth = startWidth;
44443             this.endWidth = endWidth;
44444         }
44445         Bezier.fromPoints = function (points, widths, scope) {
44446             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44447             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44448             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44449         };
44450         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44451             var dx1 = s1.x - s2.x;
44452             var dy1 = s1.y - s2.y;
44453             var dx2 = s2.x - s3.x;
44454             var dy2 = s2.y - s3.y;
44455             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44456             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44457             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44458             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44459             var dxm = m1.x - m2.x;
44460             var dym = m1.y - m2.y;
44461             var k = l2 / (l1 + l2);
44462             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44463             var tx = s2.x - cm.x;
44464             var ty = s2.y - cm.y;
44465             return {
44466                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44467                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44468             };
44469         };
44470         Bezier.prototype.length = function () {
44471             var steps = 10;
44472             var length = 0;
44473             var px;
44474             var py;
44475             for (var i = 0; i <= steps; i += 1) {
44476                 var t = i / steps;
44477                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44478                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44479                 if (i > 0) {
44480                     var xdiff = cx - px;
44481                     var ydiff = cy - py;
44482                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44483                 }
44484                 px = cx;
44485                 py = cy;
44486             }
44487             return length;
44488         };
44489         Bezier.prototype.point = function (t, start, c1, c2, end) {
44490             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44491             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44492             + (3.0 * c2 * (1.0 - t) * t * t)
44493             + (end * t * t * t);
44494         };
44495         return Bezier;
44496     }()),
44497     
44498     throttleStroke: function(fn, wait) {
44499       if (wait === void 0) { wait = 250; }
44500       var previous = 0;
44501       var timeout = null;
44502       var result;
44503       var storedContext;
44504       var storedArgs;
44505       var later = function () {
44506           previous = Date.now();
44507           timeout = null;
44508           result = fn.apply(storedContext, storedArgs);
44509           if (!timeout) {
44510               storedContext = null;
44511               storedArgs = [];
44512           }
44513       };
44514       return function wrapper() {
44515           var args = [];
44516           for (var _i = 0; _i < arguments.length; _i++) {
44517               args[_i] = arguments[_i];
44518           }
44519           var now = Date.now();
44520           var remaining = wait - (now - previous);
44521           storedContext = this;
44522           storedArgs = args;
44523           if (remaining <= 0 || remaining > wait) {
44524               if (timeout) {
44525                   clearTimeout(timeout);
44526                   timeout = null;
44527               }
44528               previous = now;
44529               result = fn.apply(storedContext, storedArgs);
44530               if (!timeout) {
44531                   storedContext = null;
44532                   storedArgs = [];
44533               }
44534           }
44535           else if (!timeout) {
44536               timeout = window.setTimeout(later, remaining);
44537           }
44538           return result;
44539       };
44540   }
44541   
44542 });
44543
44544  
44545
44546