roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); /*
18  * Based on:
19  * Ext JS Library 1.1.1
20  * Copyright(c) 2006-2007, Ext JS, LLC.
21  *
22  * Originally Released Under LGPL - original licence link has changed is not relivant.
23  *
24  * Fork - LGPL
25  * <script type="text/javascript">
26  */
27
28
29 /**
30  * @class Roo.Shadow
31  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
32  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
33  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
34  * @constructor
35  * Create a new Shadow
36  * @param {Object} config The config object
37  */
38 Roo.Shadow = function(config){
39     Roo.apply(this, config);
40     if(typeof this.mode != "string"){
41         this.mode = this.defaultMode;
42     }
43     var o = this.offset, a = {h: 0};
44     var rad = Math.floor(this.offset/2);
45     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
46         case "drop":
47             a.w = 0;
48             a.l = a.t = o;
49             a.t -= 1;
50             if(Roo.isIE){
51                 a.l -= this.offset + rad;
52                 a.t -= this.offset + rad;
53                 a.w -= rad;
54                 a.h -= rad;
55                 a.t += 1;
56             }
57         break;
58         case "sides":
59             a.w = (o*2);
60             a.l = -o;
61             a.t = o-1;
62             if(Roo.isIE){
63                 a.l -= (this.offset - rad);
64                 a.t -= this.offset + rad;
65                 a.l += 1;
66                 a.w -= (this.offset - rad)*2;
67                 a.w -= rad + 1;
68                 a.h -= 1;
69             }
70         break;
71         case "frame":
72             a.w = a.h = (o*2);
73             a.l = a.t = -o;
74             a.t += 1;
75             a.h -= 2;
76             if(Roo.isIE){
77                 a.l -= (this.offset - rad);
78                 a.t -= (this.offset - rad);
79                 a.l += 1;
80                 a.w -= (this.offset + rad + 1);
81                 a.h -= (this.offset + rad);
82                 a.h += 1;
83             }
84         break;
85     };
86
87     this.adjusts = a;
88 };
89
90 Roo.Shadow.prototype = {
91     /**
92      * @cfg {String} mode
93      * The shadow display mode.  Supports the following options:<br />
94      * sides: Shadow displays on both sides and bottom only<br />
95      * frame: Shadow displays equally on all four sides<br />
96      * drop: Traditional bottom-right drop shadow (default)
97      */
98     /**
99      * @cfg {String} offset
100      * The number of pixels to offset the shadow from the element (defaults to 4)
101      */
102     offset: 4,
103
104     // private
105     defaultMode: "drop",
106
107     /**
108      * Displays the shadow under the target element
109      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
110      */
111     show : function(target){
112         target = Roo.get(target);
113         if(!this.el){
114             this.el = Roo.Shadow.Pool.pull();
115             if(this.el.dom.nextSibling != target.dom){
116                 this.el.insertBefore(target);
117             }
118         }
119         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
120         if(Roo.isIE){
121             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
122         }
123         this.realign(
124             target.getLeft(true),
125             target.getTop(true),
126             target.getWidth(),
127             target.getHeight()
128         );
129         this.el.dom.style.display = "block";
130     },
131
132     /**
133      * Returns true if the shadow is visible, else false
134      */
135     isVisible : function(){
136         return this.el ? true : false;  
137     },
138
139     /**
140      * Direct alignment when values are already available. Show must be called at least once before
141      * calling this method to ensure it is initialized.
142      * @param {Number} left The target element left position
143      * @param {Number} top The target element top position
144      * @param {Number} width The target element width
145      * @param {Number} height The target element height
146      */
147     realign : function(l, t, w, h){
148         if(!this.el){
149             return;
150         }
151         var a = this.adjusts, d = this.el.dom, s = d.style;
152         var iea = 0;
153         s.left = (l+a.l)+"px";
154         s.top = (t+a.t)+"px";
155         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
156  
157         if(s.width != sws || s.height != shs){
158             s.width = sws;
159             s.height = shs;
160             if(!Roo.isIE){
161                 var cn = d.childNodes;
162                 var sww = Math.max(0, (sw-12))+"px";
163                 cn[0].childNodes[1].style.width = sww;
164                 cn[1].childNodes[1].style.width = sww;
165                 cn[2].childNodes[1].style.width = sww;
166                 cn[1].style.height = Math.max(0, (sh-12))+"px";
167             }
168         }
169     },
170
171     /**
172      * Hides this shadow
173      */
174     hide : function(){
175         if(this.el){
176             this.el.dom.style.display = "none";
177             Roo.Shadow.Pool.push(this.el);
178             delete this.el;
179         }
180     },
181
182     /**
183      * Adjust the z-index of this shadow
184      * @param {Number} zindex The new z-index
185      */
186     setZIndex : function(z){
187         this.zIndex = z;
188         if(this.el){
189             this.el.setStyle("z-index", z);
190         }
191     }
192 };
193
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
196     var p = [];
197     var markup = Roo.isIE ?
198                  '<div class="x-ie-shadow"></div>' :
199                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
200     return {
201         pull : function(){
202             var sh = p.shift();
203             if(!sh){
204                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205                 sh.autoBoxAdjust = false;
206             }
207             return sh;
208         },
209
210         push : function(sh){
211             p.push(sh);
212         }
213     };
214 }();/*
215  * - LGPL
216  *
217  * base class for bootstrap elements.
218  * 
219  */
220
221 Roo.bootstrap = Roo.bootstrap || {};
222 /**
223  * @class Roo.bootstrap.Component
224  * @extends Roo.Component
225  * Bootstrap Component base class
226  * @cfg {String} cls css class
227  * @cfg {String} style any extra css
228  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
230  * @cfg {string} dataId cutomer id
231  * @cfg {string} name Specifies name attribute
232  * @cfg {string} tooltip  Text for the tooltip
233  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
234  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
235  
236  * @constructor
237  * Do not use directly - it does not do anything..
238  * @param {Object} config The config object
239  */
240
241
242
243 Roo.bootstrap.Component = function(config){
244     Roo.bootstrap.Component.superclass.constructor.call(this, config);
245        
246     this.addEvents({
247         /**
248          * @event childrenrendered
249          * Fires when the children have been rendered..
250          * @param {Roo.bootstrap.Component} this
251          */
252         "childrenrendered" : true
253         
254         
255         
256     });
257     
258     
259 };
260
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
262     
263     
264     allowDomMove : false, // to stop relocations in parent onRender...
265     
266     cls : false,
267     
268     style : false,
269     
270     autoCreate : false,
271     
272     tooltip : null,
273     /**
274      * Initialize Events for the element
275      */
276     initEvents : function() { },
277     
278     xattr : false,
279     
280     parentId : false,
281     
282     can_build_overlaid : true,
283     
284     container_method : false,
285     
286     dataId : false,
287     
288     name : false,
289     
290     parent: function() {
291         // returns the parent component..
292         return Roo.ComponentMgr.get(this.parentId)
293         
294         
295     },
296     
297     // private
298     onRender : function(ct, position)
299     {
300        // Roo.log("Call onRender: " + this.xtype);
301         
302         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
303         
304         if(this.el){
305             if (this.el.attr('xtype')) {
306                 this.el.attr('xtypex', this.el.attr('xtype'));
307                 this.el.dom.removeAttribute('xtype');
308                 
309                 this.initEvents();
310             }
311             
312             return;
313         }
314         
315          
316         
317         var cfg = Roo.apply({},  this.getAutoCreate());
318         
319         cfg.id = this.id || Roo.id();
320         
321         // fill in the extra attributes 
322         if (this.xattr && typeof(this.xattr) =='object') {
323             for (var i in this.xattr) {
324                 cfg[i] = this.xattr[i];
325             }
326         }
327         
328         if(this.dataId){
329             cfg.dataId = this.dataId;
330         }
331         
332         if (this.cls) {
333             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
334         }
335         
336         if (this.style) { // fixme needs to support more complex style data.
337             cfg.style = this.style;
338         }
339         
340         if(this.name){
341             cfg.name = this.name;
342         }
343         
344         this.el = ct.createChild(cfg, position);
345         
346         if (this.tooltip) {
347             this.tooltipEl().attr('tooltip', this.tooltip);
348         }
349         
350         if(this.tabIndex !== undefined){
351             this.el.dom.setAttribute('tabIndex', this.tabIndex);
352         }
353         
354         this.initEvents();
355         
356     },
357     /**
358      * Fetch the element to add children to
359      * @return {Roo.Element} defaults to this.el
360      */
361     getChildContainer : function()
362     {
363         return this.el;
364     },
365     /**
366      * Fetch the element to display the tooltip on.
367      * @return {Roo.Element} defaults to this.el
368      */
369     tooltipEl : function()
370     {
371         return this.el;
372     },
373         
374     addxtype  : function(tree,cntr)
375     {
376         var cn = this;
377         
378         cn = Roo.factory(tree);
379         //Roo.log(['addxtype', cn]);
380            
381         cn.parentType = this.xtype; //??
382         cn.parentId = this.id;
383         
384         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
385         if (typeof(cn.container_method) == 'string') {
386             cntr = cn.container_method;
387         }
388         
389         
390         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
391         
392         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
393         
394         var build_from_html =  Roo.XComponent.build_from_html;
395           
396         var is_body  = (tree.xtype == 'Body') ;
397           
398         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
399           
400         var self_cntr_el = Roo.get(this[cntr](false));
401         
402         // do not try and build conditional elements 
403         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
404             return false;
405         }
406         
407         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
408             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
409                 return this.addxtypeChild(tree,cntr, is_body);
410             }
411             
412             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
413                 
414             if(echild){
415                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
416             }
417             
418             Roo.log('skipping render');
419             return cn;
420             
421         }
422         
423         var ret = false;
424         if (!build_from_html) {
425             return false;
426         }
427         
428         // this i think handles overlaying multiple children of the same type
429         // with the sam eelement.. - which might be buggy..
430         while (true) {
431             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
432             
433             if (!echild) {
434                 break;
435             }
436             
437             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
438                 break;
439             }
440             
441             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
442         }
443        
444         return ret;
445     },
446     
447     
448     addxtypeChild : function (tree, cntr, is_body)
449     {
450         Roo.debug && Roo.log('addxtypeChild:' + cntr);
451         var cn = this;
452         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
453         
454         
455         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
456                     (typeof(tree['flexy:foreach']) != 'undefined');
457           
458     
459         
460         skip_children = false;
461         // render the element if it's not BODY.
462         if (!is_body) {
463             
464             // if parent was disabled, then do not try and create the children..
465             if(!this[cntr](true)){
466                 tree.items = [];
467                 return tree;
468             }
469            
470             cn = Roo.factory(tree);
471            
472             cn.parentType = this.xtype; //??
473             cn.parentId = this.id;
474             
475             var build_from_html =  Roo.XComponent.build_from_html;
476             
477             
478             // does the container contain child eleemnts with 'xtype' attributes.
479             // that match this xtype..
480             // note - when we render we create these as well..
481             // so we should check to see if body has xtype set.
482             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
483                
484                 var self_cntr_el = Roo.get(this[cntr](false));
485                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
486                 if (echild) { 
487                     //Roo.log(Roo.XComponent.build_from_html);
488                     //Roo.log("got echild:");
489                     //Roo.log(echild);
490                 }
491                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
492                 // and are not displayed -this causes this to use up the wrong element when matching.
493                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
494                 
495                 
496                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
497                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
498                   
499                   
500                   
501                     cn.el = echild;
502                   //  Roo.log("GOT");
503                     //echild.dom.removeAttribute('xtype');
504                 } else {
505                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
506                     Roo.debug && Roo.log(self_cntr_el);
507                     Roo.debug && Roo.log(echild);
508                     Roo.debug && Roo.log(cn);
509                 }
510             }
511            
512             
513            
514             // if object has flexy:if - then it may or may not be rendered.
515             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
516                 // skip a flexy if element.
517                 Roo.debug && Roo.log('skipping render');
518                 Roo.debug && Roo.log(tree);
519                 if (!cn.el) {
520                     Roo.debug && Roo.log('skipping all children');
521                     skip_children = true;
522                 }
523                 
524              } else {
525                  
526                 // actually if flexy:foreach is found, we really want to create 
527                 // multiple copies here...
528                 //Roo.log('render');
529                 //Roo.log(this[cntr]());
530                 // some elements do not have render methods.. like the layouts...
531                 /*
532                 if(this[cntr](true) === false){
533                     cn.items = [];
534                     return cn;
535                 }
536                 */
537                 cn.render && cn.render(this[cntr](true));
538                 
539              }
540             // then add the element..
541         }
542          
543         // handle the kids..
544         
545         var nitems = [];
546         /*
547         if (typeof (tree.menu) != 'undefined') {
548             tree.menu.parentType = cn.xtype;
549             tree.menu.triggerEl = cn.el;
550             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
551             
552         }
553         */
554         if (!tree.items || !tree.items.length) {
555             cn.items = nitems;
556             //Roo.log(["no children", this]);
557             
558             return cn;
559         }
560          
561         var items = tree.items;
562         delete tree.items;
563         
564         //Roo.log(items.length);
565             // add the items..
566         if (!skip_children) {    
567             for(var i =0;i < items.length;i++) {
568               //  Roo.log(['add child', items[i]]);
569                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
570             }
571         }
572         
573         cn.items = nitems;
574         
575         //Roo.log("fire childrenrendered");
576         
577         cn.fireEvent('childrenrendered', this);
578         
579         return cn;
580     },
581     
582     /**
583      * Set the element that will be used to show or hide
584      */
585     setVisibilityEl : function(el)
586     {
587         this.visibilityEl = el;
588     },
589     
590      /**
591      * Get the element that will be used to show or hide
592      */
593     getVisibilityEl : function()
594     {
595         if (typeof(this.visibilityEl) == 'object') {
596             return this.visibilityEl;
597         }
598         
599         if (typeof(this.visibilityEl) == 'string') {
600             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
601         }
602         
603         return this.getEl();
604     },
605     
606     /**
607      * Show a component - removes 'hidden' class
608      */
609     show : function()
610     {
611         if(!this.getVisibilityEl()){
612             return;
613         }
614          
615         this.getVisibilityEl().removeClass(['hidden','d-none']);
616         
617         this.fireEvent('show', this);
618         
619         
620     },
621     /**
622      * Hide a component - adds 'hidden' class
623      */
624     hide: function()
625     {
626         if(!this.getVisibilityEl()){
627             return;
628         }
629         
630         this.getVisibilityEl().addClass(['hidden','d-none']);
631         
632         this.fireEvent('hide', this);
633         
634     }
635 });
636
637  /*
638  * - LGPL
639  *
640  * element
641  * 
642  */
643
644 /**
645  * @class Roo.bootstrap.Element
646  * @extends Roo.bootstrap.Component
647  * Bootstrap Element class
648  * @cfg {String} html contents of the element
649  * @cfg {String} tag tag of the element
650  * @cfg {String} cls class of the element
651  * @cfg {Boolean} preventDefault (true|false) default false
652  * @cfg {Boolean} clickable (true|false) default false
653  * 
654  * @constructor
655  * Create a new Element
656  * @param {Object} config The config object
657  */
658
659 Roo.bootstrap.Element = function(config){
660     Roo.bootstrap.Element.superclass.constructor.call(this, config);
661     
662     this.addEvents({
663         // raw events
664         /**
665          * @event click
666          * When a element is chick
667          * @param {Roo.bootstrap.Element} this
668          * @param {Roo.EventObject} e
669          */
670         "click" : true
671     });
672 };
673
674 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
675     
676     tag: 'div',
677     cls: '',
678     html: '',
679     preventDefault: false, 
680     clickable: false,
681     
682     getAutoCreate : function(){
683         
684         var cfg = {
685             tag: this.tag,
686             // cls: this.cls, double assign in parent class Component.js :: onRender
687             html: this.html
688         };
689         
690         return cfg;
691     },
692     
693     initEvents: function() 
694     {
695         Roo.bootstrap.Element.superclass.initEvents.call(this);
696         
697         if(this.clickable){
698             this.el.on('click', this.onClick, this);
699         }
700         
701     },
702     
703     onClick : function(e)
704     {
705         if(this.preventDefault){
706             e.preventDefault();
707         }
708         
709         this.fireEvent('click', this, e);
710     },
711     
712     getValue : function()
713     {
714         return this.el.dom.innerHTML;
715     },
716     
717     setValue : function(value)
718     {
719         this.el.dom.innerHTML = value;
720     }
721    
722 });
723
724  
725
726  /*
727  * - LGPL
728  *
729  * dropable area
730  * 
731  */
732
733 /**
734  * @class Roo.bootstrap.DropTarget
735  * @extends Roo.bootstrap.Element
736  * Bootstrap DropTarget class
737  
738  * @cfg {string} name dropable name
739  * 
740  * @constructor
741  * Create a new Dropable Area
742  * @param {Object} config The config object
743  */
744
745 Roo.bootstrap.DropTarget = function(config){
746     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
747     
748     this.addEvents({
749         // raw events
750         /**
751          * @event click
752          * When a element is chick
753          * @param {Roo.bootstrap.Element} this
754          * @param {Roo.EventObject} e
755          */
756         "drop" : true
757     });
758 };
759
760 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
761     
762     
763     getAutoCreate : function(){
764         
765          
766     },
767     
768     initEvents: function() 
769     {
770         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
771         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
772             ddGroup: this.name,
773             listeners : {
774                 drop : this.dragDrop.createDelegate(this),
775                 enter : this.dragEnter.createDelegate(this),
776                 out : this.dragOut.createDelegate(this),
777                 over : this.dragOver.createDelegate(this)
778             }
779             
780         });
781         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
782     },
783     
784     dragDrop : function(source,e,data)
785     {
786         // user has to decide how to impliment this.
787         Roo.log('drop');
788         Roo.log(this);
789         //this.fireEvent('drop', this, source, e ,data);
790         return false;
791     },
792     
793     dragEnter : function(n, dd, e, data)
794     {
795         // probably want to resize the element to match the dropped element..
796         Roo.log("enter");
797         this.originalSize = this.el.getSize();
798         this.el.setSize( n.el.getSize());
799         this.dropZone.DDM.refreshCache(this.name);
800         Roo.log([n, dd, e, data]);
801     },
802     
803     dragOut : function(value)
804     {
805         // resize back to normal
806         Roo.log("out");
807         this.el.setSize(this.originalSize);
808         this.dropZone.resetConstraints();
809     },
810     
811     dragOver : function()
812     {
813         // ??? do nothing?
814     }
815    
816 });
817
818  
819
820  /*
821  * - LGPL
822  *
823  * Body
824  *
825  */
826
827 /**
828  * @class Roo.bootstrap.Body
829  * @extends Roo.bootstrap.Component
830  * Bootstrap Body class
831  *
832  * @constructor
833  * Create a new body
834  * @param {Object} config The config object
835  */
836
837 Roo.bootstrap.Body = function(config){
838
839     config = config || {};
840
841     Roo.bootstrap.Body.superclass.constructor.call(this, config);
842     this.el = Roo.get(config.el ? config.el : document.body );
843     if (this.cls && this.cls.length) {
844         Roo.get(document.body).addClass(this.cls);
845     }
846 };
847
848 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
849
850     is_body : true,// just to make sure it's constructed?
851
852         autoCreate : {
853         cls: 'container'
854     },
855     onRender : function(ct, position)
856     {
857        /* Roo.log("Roo.bootstrap.Body - onRender");
858         if (this.cls && this.cls.length) {
859             Roo.get(document.body).addClass(this.cls);
860         }
861         // style??? xttr???
862         */
863     }
864
865
866
867
868 });
869 /*
870  * - LGPL
871  *
872  * button group
873  * 
874  */
875
876
877 /**
878  * @class Roo.bootstrap.ButtonGroup
879  * @extends Roo.bootstrap.Component
880  * Bootstrap ButtonGroup class
881  * @cfg {String} size lg | sm | xs (default empty normal)
882  * @cfg {String} align vertical | justified  (default none)
883  * @cfg {String} direction up | down (default down)
884  * @cfg {Boolean} toolbar false | true
885  * @cfg {Boolean} btn true | false
886  * 
887  * 
888  * @constructor
889  * Create a new Input
890  * @param {Object} config The config object
891  */
892
893 Roo.bootstrap.ButtonGroup = function(config){
894     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
895 };
896
897 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
898     
899     size: '',
900     align: '',
901     direction: '',
902     toolbar: false,
903     btn: true,
904
905     getAutoCreate : function(){
906         var cfg = {
907             cls: 'btn-group',
908             html : null
909         };
910         
911         cfg.html = this.html || cfg.html;
912         
913         if (this.toolbar) {
914             cfg = {
915                 cls: 'btn-toolbar',
916                 html: null
917             };
918             
919             return cfg;
920         }
921         
922         if (['vertical','justified'].indexOf(this.align)!==-1) {
923             cfg.cls = 'btn-group-' + this.align;
924             
925             if (this.align == 'justified') {
926                 console.log(this.items);
927             }
928         }
929         
930         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
931             cfg.cls += ' btn-group-' + this.size;
932         }
933         
934         if (this.direction == 'up') {
935             cfg.cls += ' dropup' ;
936         }
937         
938         return cfg;
939     },
940     /**
941      * Add a button to the group (similar to NavItem API.)
942      */
943     addItem : function(cfg)
944     {
945         var cn = new Roo.bootstrap.Button(cfg);
946         //this.register(cn);
947         cn.parentId = this.id;
948         cn.onRender(this.el, null);
949         return cn;
950     }
951    
952 });
953
954  /*
955  * - LGPL
956  *
957  * button
958  * 
959  */
960
961 /**
962  * @class Roo.bootstrap.Button
963  * @extends Roo.bootstrap.Component
964  * Bootstrap Button class
965  * @cfg {String} html The button content
966  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
967  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
968  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
969  * @cfg {String} size (lg|sm|xs)
970  * @cfg {String} tag (a|input|submit)
971  * @cfg {String} href empty or href
972  * @cfg {Boolean} disabled default false;
973  * @cfg {Boolean} isClose default false;
974  * @cfg {String} glyphicon depricated - use fa
975  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
976  * @cfg {String} badge text for badge
977  * @cfg {String} theme (default|glow)  
978  * @cfg {Boolean} inverse dark themed version
979  * @cfg {Boolean} toggle is it a slidy toggle button
980  * @cfg {Boolean} pressed   default null - if the button ahs active state
981  * @cfg {String} ontext text for on slidy toggle state
982  * @cfg {String} offtext text for off slidy toggle state
983  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
984  * @cfg {Boolean} removeClass remove the standard class..
985  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
986  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
987  * 
988  * @constructor
989  * Create a new button
990  * @param {Object} config The config object
991  */
992
993
994 Roo.bootstrap.Button = function(config){
995     Roo.bootstrap.Button.superclass.constructor.call(this, config);
996     
997     this.addEvents({
998         // raw events
999         /**
1000          * @event click
1001          * When a button is pressed
1002          * @param {Roo.bootstrap.Button} btn
1003          * @param {Roo.EventObject} e
1004          */
1005         "click" : true,
1006         /**
1007          * @event dblclick
1008          * When a button is double clicked
1009          * @param {Roo.bootstrap.Button} btn
1010          * @param {Roo.EventObject} e
1011          */
1012         "dblclick" : true,
1013          /**
1014          * @event toggle
1015          * After the button has been toggles
1016          * @param {Roo.bootstrap.Button} btn
1017          * @param {Roo.EventObject} e
1018          * @param {boolean} pressed (also available as button.pressed)
1019          */
1020         "toggle" : true
1021     });
1022 };
1023
1024 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1025     html: false,
1026     active: false,
1027     weight: '',
1028     badge_weight: '',
1029     outline : false,
1030     size: '',
1031     tag: 'button',
1032     href: '',
1033     disabled: false,
1034     isClose: false,
1035     glyphicon: '',
1036     fa: '',
1037     badge: '',
1038     theme: 'default',
1039     inverse: false,
1040     
1041     toggle: false,
1042     ontext: 'ON',
1043     offtext: 'OFF',
1044     defaulton: true,
1045     preventDefault: true,
1046     removeClass: false,
1047     name: false,
1048     target: false,
1049     group : false,
1050      
1051     pressed : null,
1052      
1053     
1054     getAutoCreate : function(){
1055         
1056         var cfg = {
1057             tag : 'button',
1058             cls : 'roo-button',
1059             html: ''
1060         };
1061         
1062         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1063             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1064             this.tag = 'button';
1065         } else {
1066             cfg.tag = this.tag;
1067         }
1068         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1069         
1070         if (this.toggle == true) {
1071             cfg={
1072                 tag: 'div',
1073                 cls: 'slider-frame roo-button',
1074                 cn: [
1075                     {
1076                         tag: 'span',
1077                         'data-on-text':'ON',
1078                         'data-off-text':'OFF',
1079                         cls: 'slider-button',
1080                         html: this.offtext
1081                     }
1082                 ]
1083             };
1084             // why are we validating the weights?
1085             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1086                 cfg.cls +=  ' ' + this.weight;
1087             }
1088             
1089             return cfg;
1090         }
1091         
1092         if (this.isClose) {
1093             cfg.cls += ' close';
1094             
1095             cfg["aria-hidden"] = true;
1096             
1097             cfg.html = "&times;";
1098             
1099             return cfg;
1100         }
1101              
1102         
1103         if (this.theme==='default') {
1104             cfg.cls = 'btn roo-button';
1105             
1106             //if (this.parentType != 'Navbar') {
1107             this.weight = this.weight.length ?  this.weight : 'default';
1108             //}
1109             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1110                 
1111                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1112                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1113                 cfg.cls += ' btn-' + outline + weight;
1114                 if (this.weight == 'default') {
1115                     // BC
1116                     cfg.cls += ' btn-' + this.weight;
1117                 }
1118             }
1119         } else if (this.theme==='glow') {
1120             
1121             cfg.tag = 'a';
1122             cfg.cls = 'btn-glow roo-button';
1123             
1124             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1125                 
1126                 cfg.cls += ' ' + this.weight;
1127             }
1128         }
1129    
1130         
1131         if (this.inverse) {
1132             this.cls += ' inverse';
1133         }
1134         
1135         
1136         if (this.active || this.pressed === true) {
1137             cfg.cls += ' active';
1138         }
1139         
1140         if (this.disabled) {
1141             cfg.disabled = 'disabled';
1142         }
1143         
1144         if (this.items) {
1145             Roo.log('changing to ul' );
1146             cfg.tag = 'ul';
1147             this.glyphicon = 'caret';
1148             if (Roo.bootstrap.version == 4) {
1149                 this.fa = 'caret-down';
1150             }
1151             
1152         }
1153         
1154         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1155          
1156         //gsRoo.log(this.parentType);
1157         if (this.parentType === 'Navbar' && !this.parent().bar) {
1158             Roo.log('changing to li?');
1159             
1160             cfg.tag = 'li';
1161             
1162             cfg.cls = '';
1163             cfg.cn =  [{
1164                 tag : 'a',
1165                 cls : 'roo-button',
1166                 html : this.html,
1167                 href : this.href || '#'
1168             }];
1169             if (this.menu) {
1170                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1171                 cfg.cls += ' dropdown';
1172             }   
1173             
1174             delete cfg.html;
1175             
1176         }
1177         
1178        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1179         
1180         if (this.glyphicon) {
1181             cfg.html = ' ' + cfg.html;
1182             
1183             cfg.cn = [
1184                 {
1185                     tag: 'span',
1186                     cls: 'glyphicon glyphicon-' + this.glyphicon
1187                 }
1188             ];
1189         }
1190         if (this.fa) {
1191             cfg.html = ' ' + cfg.html;
1192             
1193             cfg.cn = [
1194                 {
1195                     tag: 'i',
1196                     cls: 'fa fas fa-' + this.fa
1197                 }
1198             ];
1199         }
1200         
1201         if (this.badge) {
1202             cfg.html += ' ';
1203             
1204             cfg.tag = 'a';
1205             
1206 //            cfg.cls='btn roo-button';
1207             
1208             cfg.href=this.href;
1209             
1210             var value = cfg.html;
1211             
1212             if(this.glyphicon){
1213                 value = {
1214                     tag: 'span',
1215                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1216                     html: this.html
1217                 };
1218             }
1219             if(this.fa){
1220                 value = {
1221                     tag: 'i',
1222                     cls: 'fa fas fa-' + this.fa,
1223                     html: this.html
1224                 };
1225             }
1226             
1227             var bw = this.badge_weight.length ? this.badge_weight :
1228                 (this.weight.length ? this.weight : 'secondary');
1229             bw = bw == 'default' ? 'secondary' : bw;
1230             
1231             cfg.cn = [
1232                 value,
1233                 {
1234                     tag: 'span',
1235                     cls: 'badge badge-' + bw,
1236                     html: this.badge
1237                 }
1238             ];
1239             
1240             cfg.html='';
1241         }
1242         
1243         if (this.menu) {
1244             cfg.cls += ' dropdown';
1245             cfg.html = typeof(cfg.html) != 'undefined' ?
1246                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1247         }
1248         
1249         if (cfg.tag !== 'a' && this.href !== '') {
1250             throw "Tag must be a to set href.";
1251         } else if (this.href.length > 0) {
1252             cfg.href = this.href;
1253         }
1254         
1255         if(this.removeClass){
1256             cfg.cls = '';
1257         }
1258         
1259         if(this.target){
1260             cfg.target = this.target;
1261         }
1262         
1263         return cfg;
1264     },
1265     initEvents: function() {
1266        // Roo.log('init events?');
1267 //        Roo.log(this.el.dom);
1268         // add the menu...
1269         
1270         if (typeof (this.menu) != 'undefined') {
1271             this.menu.parentType = this.xtype;
1272             this.menu.triggerEl = this.el;
1273             this.addxtype(Roo.apply({}, this.menu));
1274         }
1275
1276
1277         if (this.el.hasClass('roo-button')) {
1278              this.el.on('click', this.onClick, this);
1279              this.el.on('dblclick', this.onDblClick, this);
1280         } else {
1281              this.el.select('.roo-button').on('click', this.onClick, this);
1282              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1283              
1284         }
1285         // why?
1286         if(this.removeClass){
1287             this.el.on('click', this.onClick, this);
1288         }
1289         
1290         if (this.group === true) {
1291              if (this.pressed === false || this.pressed === true) {
1292                 // nothing
1293             } else {
1294                 this.pressed = false;
1295                 this.setActive(this.pressed);
1296             }
1297             
1298         }
1299         
1300         this.el.enableDisplayMode();
1301         
1302     },
1303     onClick : function(e)
1304     {
1305         if (this.disabled) {
1306             return;
1307         }
1308         
1309         Roo.log('button on click ');
1310         if(this.preventDefault){
1311             e.preventDefault();
1312         }
1313         
1314         if (this.group) {
1315             if (this.pressed) {
1316                 // do nothing -
1317                 return;
1318             }
1319             this.setActive(true);
1320             var pi = this.parent().items;
1321             for (var i = 0;i < pi.length;i++) {
1322                 if (this == pi[i]) {
1323                     continue;
1324                 }
1325                 if (pi[i].el.hasClass('roo-button')) {
1326                     pi[i].setActive(false);
1327                 }
1328             }
1329             this.fireEvent('click', this, e);            
1330             return;
1331         }
1332         
1333         if (this.pressed === true || this.pressed === false) {
1334             this.toggleActive(e);
1335         }
1336         
1337         
1338         this.fireEvent('click', this, e);
1339     },
1340     onDblClick: function(e)
1341     {
1342         if (this.disabled) {
1343             return;
1344         }
1345         if(this.preventDefault){
1346             e.preventDefault();
1347         }
1348         this.fireEvent('dblclick', this, e);
1349     },
1350     /**
1351      * Enables this button
1352      */
1353     enable : function()
1354     {
1355         this.disabled = false;
1356         this.el.removeClass('disabled');
1357     },
1358     
1359     /**
1360      * Disable this button
1361      */
1362     disable : function()
1363     {
1364         this.disabled = true;
1365         this.el.addClass('disabled');
1366     },
1367      /**
1368      * sets the active state on/off, 
1369      * @param {Boolean} state (optional) Force a particular state
1370      */
1371     setActive : function(v) {
1372         
1373         this.el[v ? 'addClass' : 'removeClass']('active');
1374         this.pressed = v;
1375     },
1376      /**
1377      * toggles the current active state 
1378      */
1379     toggleActive : function(e)
1380     {
1381         this.setActive(!this.pressed); // this modifies pressed...
1382         this.fireEvent('toggle', this, e, this.pressed);
1383     },
1384      /**
1385      * get the current active state
1386      * @return {boolean} true if it's active
1387      */
1388     isActive : function()
1389     {
1390         return this.el.hasClass('active');
1391     },
1392     /**
1393      * set the text of the first selected button
1394      */
1395     setText : function(str)
1396     {
1397         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1398     },
1399     /**
1400      * get the text of the first selected button
1401      */
1402     getText : function()
1403     {
1404         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1405     },
1406     
1407     setWeight : function(str)
1408     {
1409         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1410         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1411         this.weight = str;
1412         var outline = this.outline ? 'outline-' : '';
1413         if (str == 'default') {
1414             this.el.addClass('btn-default btn-outline-secondary');        
1415             return;
1416         }
1417         this.el.addClass('btn-' + outline + str);        
1418     }
1419     
1420     
1421 });
1422 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1423
1424 Roo.bootstrap.Button.weights = [
1425     'default',
1426     'secondary' ,
1427     'primary',
1428     'success',
1429     'info',
1430     'warning',
1431     'danger',
1432     'link',
1433     'light',
1434     'dark'              
1435    
1436 ];/*
1437  * - LGPL
1438  *
1439  * column
1440  * 
1441  */
1442
1443 /**
1444  * @class Roo.bootstrap.Column
1445  * @extends Roo.bootstrap.Component
1446  * Bootstrap Column class
1447  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1448  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1449  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1450  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1451  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1452  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1453  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1454  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1455  *
1456  * 
1457  * @cfg {Boolean} hidden (true|false) hide the element
1458  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1459  * @cfg {String} fa (ban|check|...) font awesome icon
1460  * @cfg {Number} fasize (1|2|....) font awsome size
1461
1462  * @cfg {String} icon (info-sign|check|...) glyphicon name
1463
1464  * @cfg {String} html content of column.
1465  * 
1466  * @constructor
1467  * Create a new Column
1468  * @param {Object} config The config object
1469  */
1470
1471 Roo.bootstrap.Column = function(config){
1472     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1473 };
1474
1475 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1476     
1477     xs: false,
1478     sm: false,
1479     md: false,
1480     lg: false,
1481     xsoff: false,
1482     smoff: false,
1483     mdoff: false,
1484     lgoff: false,
1485     html: '',
1486     offset: 0,
1487     alert: false,
1488     fa: false,
1489     icon : false,
1490     hidden : false,
1491     fasize : 1,
1492     
1493     getAutoCreate : function(){
1494         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1495         
1496         cfg = {
1497             tag: 'div',
1498             cls: 'column'
1499         };
1500         
1501         var settings=this;
1502         var sizes =   ['xs','sm','md','lg'];
1503         sizes.map(function(size ,ix){
1504             //Roo.log( size + ':' + settings[size]);
1505             
1506             if (settings[size+'off'] !== false) {
1507                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1508             }
1509             
1510             if (settings[size] === false) {
1511                 return;
1512             }
1513             
1514             if (!settings[size]) { // 0 = hidden
1515                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1516                 // bootsrap4
1517                 for (var i = ix; i > -1; i--) {
1518                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1519                 }
1520                 
1521                 
1522                 return;
1523             }
1524             cfg.cls += ' col-' + size + '-' + settings[size] + (
1525                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1526             );
1527             
1528         });
1529         
1530         if (this.hidden) {
1531             cfg.cls += ' hidden';
1532         }
1533         
1534         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1535             cfg.cls +=' alert alert-' + this.alert;
1536         }
1537         
1538         
1539         if (this.html.length) {
1540             cfg.html = this.html;
1541         }
1542         if (this.fa) {
1543             var fasize = '';
1544             if (this.fasize > 1) {
1545                 fasize = ' fa-' + this.fasize + 'x';
1546             }
1547             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1548             
1549             
1550         }
1551         if (this.icon) {
1552             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1553         }
1554         
1555         return cfg;
1556     }
1557    
1558 });
1559
1560  
1561
1562  /*
1563  * - LGPL
1564  *
1565  * page container.
1566  * 
1567  */
1568
1569
1570 /**
1571  * @class Roo.bootstrap.Container
1572  * @extends Roo.bootstrap.Component
1573  * Bootstrap Container class
1574  * @cfg {Boolean} jumbotron is it a jumbotron element
1575  * @cfg {String} html content of element
1576  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1577  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1578  * @cfg {String} header content of header (for panel)
1579  * @cfg {String} footer content of footer (for panel)
1580  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1581  * @cfg {String} tag (header|aside|section) type of HTML tag.
1582  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1583  * @cfg {String} fa font awesome icon
1584  * @cfg {String} icon (info-sign|check|...) glyphicon name
1585  * @cfg {Boolean} hidden (true|false) hide the element
1586  * @cfg {Boolean} expandable (true|false) default false
1587  * @cfg {Boolean} expanded (true|false) default true
1588  * @cfg {String} rheader contet on the right of header
1589  * @cfg {Boolean} clickable (true|false) default false
1590
1591  *     
1592  * @constructor
1593  * Create a new Container
1594  * @param {Object} config The config object
1595  */
1596
1597 Roo.bootstrap.Container = function(config){
1598     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1599     
1600     this.addEvents({
1601         // raw events
1602          /**
1603          * @event expand
1604          * After the panel has been expand
1605          * 
1606          * @param {Roo.bootstrap.Container} this
1607          */
1608         "expand" : true,
1609         /**
1610          * @event collapse
1611          * After the panel has been collapsed
1612          * 
1613          * @param {Roo.bootstrap.Container} this
1614          */
1615         "collapse" : true,
1616         /**
1617          * @event click
1618          * When a element is chick
1619          * @param {Roo.bootstrap.Container} this
1620          * @param {Roo.EventObject} e
1621          */
1622         "click" : true
1623     });
1624 };
1625
1626 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1627     
1628     jumbotron : false,
1629     well: '',
1630     panel : '',
1631     header: '',
1632     footer : '',
1633     sticky: '',
1634     tag : false,
1635     alert : false,
1636     fa: false,
1637     icon : false,
1638     expandable : false,
1639     rheader : '',
1640     expanded : true,
1641     clickable: false,
1642   
1643      
1644     getChildContainer : function() {
1645         
1646         if(!this.el){
1647             return false;
1648         }
1649         
1650         if (this.panel.length) {
1651             return this.el.select('.panel-body',true).first();
1652         }
1653         
1654         return this.el;
1655     },
1656     
1657     
1658     getAutoCreate : function(){
1659         
1660         var cfg = {
1661             tag : this.tag || 'div',
1662             html : '',
1663             cls : ''
1664         };
1665         if (this.jumbotron) {
1666             cfg.cls = 'jumbotron';
1667         }
1668         
1669         
1670         
1671         // - this is applied by the parent..
1672         //if (this.cls) {
1673         //    cfg.cls = this.cls + '';
1674         //}
1675         
1676         if (this.sticky.length) {
1677             
1678             var bd = Roo.get(document.body);
1679             if (!bd.hasClass('bootstrap-sticky')) {
1680                 bd.addClass('bootstrap-sticky');
1681                 Roo.select('html',true).setStyle('height', '100%');
1682             }
1683              
1684             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1685         }
1686         
1687         
1688         if (this.well.length) {
1689             switch (this.well) {
1690                 case 'lg':
1691                 case 'sm':
1692                     cfg.cls +=' well well-' +this.well;
1693                     break;
1694                 default:
1695                     cfg.cls +=' well';
1696                     break;
1697             }
1698         }
1699         
1700         if (this.hidden) {
1701             cfg.cls += ' hidden';
1702         }
1703         
1704         
1705         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1706             cfg.cls +=' alert alert-' + this.alert;
1707         }
1708         
1709         var body = cfg;
1710         
1711         if (this.panel.length) {
1712             cfg.cls += ' panel panel-' + this.panel;
1713             cfg.cn = [];
1714             if (this.header.length) {
1715                 
1716                 var h = [];
1717                 
1718                 if(this.expandable){
1719                     
1720                     cfg.cls = cfg.cls + ' expandable';
1721                     
1722                     h.push({
1723                         tag: 'i',
1724                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1725                     });
1726                     
1727                 }
1728                 
1729                 h.push(
1730                     {
1731                         tag: 'span',
1732                         cls : 'panel-title',
1733                         html : (this.expandable ? '&nbsp;' : '') + this.header
1734                     },
1735                     {
1736                         tag: 'span',
1737                         cls: 'panel-header-right',
1738                         html: this.rheader
1739                     }
1740                 );
1741                 
1742                 cfg.cn.push({
1743                     cls : 'panel-heading',
1744                     style : this.expandable ? 'cursor: pointer' : '',
1745                     cn : h
1746                 });
1747                 
1748             }
1749             
1750             body = false;
1751             cfg.cn.push({
1752                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1753                 html : this.html
1754             });
1755             
1756             
1757             if (this.footer.length) {
1758                 cfg.cn.push({
1759                     cls : 'panel-footer',
1760                     html : this.footer
1761                     
1762                 });
1763             }
1764             
1765         }
1766         
1767         if (body) {
1768             body.html = this.html || cfg.html;
1769             // prefix with the icons..
1770             if (this.fa) {
1771                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1772             }
1773             if (this.icon) {
1774                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1775             }
1776             
1777             
1778         }
1779         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1780             cfg.cls =  'container';
1781         }
1782         
1783         return cfg;
1784     },
1785     
1786     initEvents: function() 
1787     {
1788         if(this.expandable){
1789             var headerEl = this.headerEl();
1790         
1791             if(headerEl){
1792                 headerEl.on('click', this.onToggleClick, this);
1793             }
1794         }
1795         
1796         if(this.clickable){
1797             this.el.on('click', this.onClick, this);
1798         }
1799         
1800     },
1801     
1802     onToggleClick : function()
1803     {
1804         var headerEl = this.headerEl();
1805         
1806         if(!headerEl){
1807             return;
1808         }
1809         
1810         if(this.expanded){
1811             this.collapse();
1812             return;
1813         }
1814         
1815         this.expand();
1816     },
1817     
1818     expand : function()
1819     {
1820         if(this.fireEvent('expand', this)) {
1821             
1822             this.expanded = true;
1823             
1824             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1825             
1826             this.el.select('.panel-body',true).first().removeClass('hide');
1827             
1828             var toggleEl = this.toggleEl();
1829
1830             if(!toggleEl){
1831                 return;
1832             }
1833
1834             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1835         }
1836         
1837     },
1838     
1839     collapse : function()
1840     {
1841         if(this.fireEvent('collapse', this)) {
1842             
1843             this.expanded = false;
1844             
1845             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1846             this.el.select('.panel-body',true).first().addClass('hide');
1847         
1848             var toggleEl = this.toggleEl();
1849
1850             if(!toggleEl){
1851                 return;
1852             }
1853
1854             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1855         }
1856     },
1857     
1858     toggleEl : function()
1859     {
1860         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1861             return;
1862         }
1863         
1864         return this.el.select('.panel-heading .fa',true).first();
1865     },
1866     
1867     headerEl : function()
1868     {
1869         if(!this.el || !this.panel.length || !this.header.length){
1870             return;
1871         }
1872         
1873         return this.el.select('.panel-heading',true).first()
1874     },
1875     
1876     bodyEl : function()
1877     {
1878         if(!this.el || !this.panel.length){
1879             return;
1880         }
1881         
1882         return this.el.select('.panel-body',true).first()
1883     },
1884     
1885     titleEl : function()
1886     {
1887         if(!this.el || !this.panel.length || !this.header.length){
1888             return;
1889         }
1890         
1891         return this.el.select('.panel-title',true).first();
1892     },
1893     
1894     setTitle : function(v)
1895     {
1896         var titleEl = this.titleEl();
1897         
1898         if(!titleEl){
1899             return;
1900         }
1901         
1902         titleEl.dom.innerHTML = v;
1903     },
1904     
1905     getTitle : function()
1906     {
1907         
1908         var titleEl = this.titleEl();
1909         
1910         if(!titleEl){
1911             return '';
1912         }
1913         
1914         return titleEl.dom.innerHTML;
1915     },
1916     
1917     setRightTitle : function(v)
1918     {
1919         var t = this.el.select('.panel-header-right',true).first();
1920         
1921         if(!t){
1922             return;
1923         }
1924         
1925         t.dom.innerHTML = v;
1926     },
1927     
1928     onClick : function(e)
1929     {
1930         e.preventDefault();
1931         
1932         this.fireEvent('click', this, e);
1933     }
1934 });
1935
1936  /*
1937  *  - LGPL
1938  *
1939  *  This is BS4's Card element.. - similar to our containers probably..
1940  * 
1941  */
1942 /**
1943  * @class Roo.bootstrap.Card
1944  * @extends Roo.bootstrap.Component
1945  * Bootstrap Card class
1946  *
1947  *
1948  * possible... may not be implemented..
1949  * @cfg {String} header_image  src url of image.
1950  * @cfg {String|Object} header
1951  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1952  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1953  * 
1954  * @cfg {String} title
1955  * @cfg {String} subtitle
1956  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1957  * @cfg {String} footer
1958  
1959  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1960  * 
1961  * @cfg {String} margin (0|1|2|3|4|5|auto)
1962  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1963  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1964  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1965  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1966  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1967  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1968  *
1969  * @cfg {String} padding (0|1|2|3|4|5)
1970  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1971  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1972  * @cfg {String} padding_left (0|1|2|3|4|5)
1973  * @cfg {String} padding_right (0|1|2|3|4|5)
1974  * @cfg {String} padding_x (0|1|2|3|4|5)
1975  * @cfg {String} padding_y (0|1|2|3|4|5)
1976  *
1977  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1978  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1979  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1980  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1981  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1982  
1983  * @config {Boolean} dragable  if this card can be dragged.
1984  * @config {String} drag_group  group for drag
1985  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
1986  * @config {String} drop_group  group for drag
1987  * 
1988  * @config {Boolean} collapsable can the body be collapsed.
1989  * @config {Boolean} collapsed is the body collapsed when rendered...
1990  * @config {Boolean} rotateable can the body be rotated by clicking on it..
1991  * @config {Boolean} rotated is the body rotated when rendered...
1992  * 
1993  * @constructor
1994  * Create a new Container
1995  * @param {Object} config The config object
1996  */
1997
1998 Roo.bootstrap.Card = function(config){
1999     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2000     
2001     this.addEvents({
2002          // raw events
2003         /**
2004          * @event drop
2005          * When a element a card is dropped
2006          * @param {Roo.bootstrap.Card} this
2007          *
2008          * 
2009          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2010          * @param {String} position 'above' or 'below'
2011          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2012         
2013          */
2014         'drop' : true,
2015          /**
2016          * @event rotate
2017          * When a element a card is rotate
2018          * @param {Roo.bootstrap.Element} this
2019          * @param {Roo.Element} n the node being dropped?
2020          * @param {Boolean} rotate status
2021          */
2022         'rotate' : true
2023         
2024     });
2025 };
2026
2027
2028 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2029     
2030     
2031     weight : '',
2032     
2033     margin: '', /// may be better in component?
2034     margin_top: '', 
2035     margin_bottom: '', 
2036     margin_left: '',
2037     margin_right: '',
2038     margin_x: '',
2039     margin_y: '',
2040     
2041     padding : '',
2042     padding_top: '', 
2043     padding_bottom: '', 
2044     padding_left: '',
2045     padding_right: '',
2046     padding_x: '',
2047     padding_y: '',
2048     
2049     display: '', 
2050     display_xs: '', 
2051     display_sm: '', 
2052     display_lg: '',
2053     display_xl: '',
2054  
2055     header_image  : '',
2056     header : '',
2057     header_size : 0,
2058     title : '',
2059     subtitle : '',
2060     html : '',
2061     footer: '',
2062
2063     collapsable : false,
2064     collapsed : false,
2065     rotateable : false,
2066     rotated : false,
2067     
2068     dragable : false,
2069     drag_group : false,
2070     dropable : false,
2071     drop_group : false,
2072     childContainer : false,
2073     dropEl : false, /// the dom placeholde element that indicates drop location.
2074     containerEl: false, // body container
2075     bodyEl: false, // card-body
2076     headerContainerEl : false, //
2077     headerEl : false,
2078     
2079     layoutCls : function()
2080     {
2081         var cls = '';
2082         var t = this;
2083         Roo.log(this.margin_bottom.length);
2084         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2085             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2086             
2087             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2088                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2089             }
2090             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2091                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2092             }
2093         });
2094         
2095         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2096             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2097                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2098             }
2099         });
2100         
2101         // more generic support?
2102         if (this.hidden) {
2103             cls += ' d-none';
2104         }
2105         
2106         return cls;
2107     },
2108  
2109        // Roo.log("Call onRender: " + this.xtype);
2110         /*  We are looking at something like this.
2111 <div class="card">
2112     <img src="..." class="card-img-top" alt="...">
2113     <div class="card-body">
2114         <h5 class="card-title">Card title</h5>
2115          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2116
2117         >> this bit is really the body...
2118         <div> << we will ad dthis in hopefully it will not break shit.
2119         
2120         ** card text does not actually have any styling...
2121         
2122             <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2123         
2124         </div> <<
2125           <a href="#" class="card-link">Card link</a>
2126           
2127     </div>
2128     <div class="card-footer">
2129         <small class="text-muted">Last updated 3 mins ago</small>
2130     </div>
2131 </div>
2132          */
2133     getAutoCreate : function(){
2134         
2135         var cfg = {
2136             tag : 'div',
2137             cls : 'card',
2138             cn : [ ]
2139         };
2140         
2141         if (this.weight.length && this.weight != 'light') {
2142             cfg.cls += ' text-white';
2143         } else {
2144             cfg.cls += ' text-dark'; // need as it's nested..
2145         }
2146         if (this.weight.length) {
2147             cfg.cls += ' bg-' + this.weight;
2148         }
2149         
2150         cfg.cls += ' ' + this.layoutCls(); 
2151         
2152         var hdr = false;
2153         var hdr_ctr = false;
2154         if (this.header.length) {
2155             hdr = {
2156                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2157                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2158                 cn : []
2159             };
2160             cfg.cn.push(hdr);
2161             hdr_ctr = hdr;
2162         } else {
2163             hdr = {
2164                 tag : 'div',
2165                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2166                 cn : []
2167             };
2168             cfg.cn.push(hdr);
2169             hdr_ctr = hdr;
2170         }
2171         if (this.collapsable) {
2172             hdr_ctr = {
2173             tag : 'a',
2174             cls : 'd-block user-select-none',
2175             cn: [
2176                     {
2177                         tag: 'i',
2178                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2179                     }
2180                    
2181                 ]
2182             };
2183             hdr.cn.push(hdr_ctr);
2184         }
2185         
2186         hdr_ctr.cn.push(        {
2187             tag: 'span',
2188             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2189             html : this.header
2190         });
2191         
2192         
2193         if (this.header_image.length) {
2194             cfg.cn.push({
2195                 tag : 'img',
2196                 cls : 'card-img-top',
2197                 src: this.header_image // escape?
2198             });
2199         } else {
2200             cfg.cn.push({
2201                     tag : 'div',
2202                     cls : 'card-img-top d-none' 
2203                 });
2204         }
2205             
2206         var body = {
2207             tag : 'div',
2208             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2209             cn : []
2210         };
2211         var obody = body;
2212         if (this.collapsable || this.rotateable) {
2213             obody = {
2214                 tag: 'div',
2215                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2216                 cn : [  body ]
2217             };
2218         }
2219         
2220         cfg.cn.push(obody);
2221         
2222         if (this.title.length) {
2223             body.cn.push({
2224                 tag : 'div',
2225                 cls : 'card-title',
2226                 src: this.title // escape?
2227             });
2228         }  
2229         
2230         if (this.subtitle.length) {
2231             body.cn.push({
2232                 tag : 'div',
2233                 cls : 'card-title',
2234                 src: this.subtitle // escape?
2235             });
2236         }
2237         
2238         body.cn.push({
2239             tag : 'div',
2240             cls : 'roo-card-body-ctr'
2241         });
2242         
2243         if (this.html.length) {
2244             body.cn.push({
2245                 tag: 'div',
2246                 html : this.html
2247             });
2248         }
2249         // fixme ? handle objects?
2250         
2251         if (this.footer.length) {
2252            
2253             cfg.cn.push({
2254                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2255                 html : this.footer
2256             });
2257             
2258         } else {
2259             cfg.cn.push({cls : 'card-footer d-none'});
2260         }
2261         
2262         // footer...
2263         
2264         return cfg;
2265     },
2266     
2267     
2268     getCardHeader : function()
2269     {
2270         var  ret = this.el.select('.card-header',true).first();
2271         if (ret.hasClass('d-none')) {
2272             ret.removeClass('d-none');
2273         }
2274         
2275         return ret;
2276     },
2277     getCardFooter : function()
2278     {
2279         var  ret = this.el.select('.card-footer',true).first();
2280         if (ret.hasClass('d-none')) {
2281             ret.removeClass('d-none');
2282         }
2283         
2284         return ret;
2285     },
2286     getCardImageTop : function()
2287     {
2288         var  ret = this.el.select('.card-img-top',true).first();
2289         if (ret.hasClass('d-none')) {
2290             ret.removeClass('d-none');
2291         }
2292             
2293         return ret;
2294     },
2295     
2296     getChildContainer : function()
2297     {
2298         
2299         if(!this.el){
2300             return false;
2301         }
2302         return this.el.select('.roo-card-body-ctr',true).first();    
2303     },
2304     
2305     initEvents: function() 
2306     {
2307         this.bodyEl = this.el.select('.card-body',true).first(); 
2308         this.containerEl = this.getChildContainer();
2309         if(this.dragable){
2310             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2311                     containerScroll: true,
2312                     ddGroup: this.drag_group || 'default_card_drag_group'
2313             });
2314             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2315         }
2316         if (this.dropable) {
2317             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2318                 containerScroll: true,
2319                 ddGroup: this.drop_group || 'default_card_drag_group'
2320             });
2321             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2322             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2323             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2324             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2325             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2326         }
2327         
2328         if (this.collapsable) {
2329             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2330         }
2331         if (this.rotateable) {
2332             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2333         }
2334         this.collapsableEl = this.el.select('.roo-collapsable').first();
2335          
2336         this.footerEl = this.el.select('.card-footer').first();
2337         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2338         this.headerContainerEl = this.el.select('.roo-card-header-ctr').first();
2339         this.headerEl = this.el.select('.card-header',true).first();
2340         
2341         if (this.rotated) {
2342             this.el.addClass('roo-card-rotated');
2343             this.fireEvent('rotate', this, true);
2344         }
2345         
2346     },
2347     getDragData : function(e)
2348     {
2349         var target = this.getEl();
2350         if (target) {
2351             //this.handleSelection(e);
2352             
2353             var dragData = {
2354                 source: this,
2355                 copy: false,
2356                 nodes: this.getEl(),
2357                 records: []
2358             };
2359             
2360             
2361             dragData.ddel = target.dom ;    // the div element
2362             Roo.log(target.getWidth( ));
2363             dragData.ddel.style.width = target.getWidth() + 'px';
2364             
2365             return dragData;
2366         }
2367         return false;
2368     },
2369     /**
2370     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2371     *    whole Element becomes the target, and this causes the drop gesture to append.
2372     */
2373     getTargetFromEvent : function(e, dragged_card_el)
2374     {
2375         var target = e.getTarget();
2376         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2377             target = target.parentNode;
2378         }
2379         
2380         var ret = {
2381             position: '',
2382             cards : [],
2383             card_n : -1,
2384             items_n : -1,
2385             card : false 
2386         };
2387         
2388         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2389         // see if target is one of the 'cards'...
2390         
2391         
2392         //Roo.log(this.items.length);
2393         var pos = false;
2394         
2395         var last_card_n = 0;
2396         var cards_len  = 0;
2397         for (var i = 0;i< this.items.length;i++) {
2398             
2399             if (!this.items[i].el.hasClass('card')) {
2400                  continue;
2401             }
2402             pos = this.getDropPoint(e, this.items[i].el.dom);
2403             
2404             cards_len = ret.cards.length;
2405             //Roo.log(this.items[i].el.dom.id);
2406             ret.cards.push(this.items[i]);
2407             last_card_n  = i;
2408             if (ret.card_n < 0 && pos == 'above') {
2409                 ret.position = cards_len > 0 ? 'below' : pos;
2410                 ret.items_n = i > 0 ? i - 1 : 0;
2411                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2412                 ret.card = ret.cards[ret.card_n];
2413             }
2414         }
2415         if (!ret.cards.length) {
2416             ret.card = true;
2417             ret.position = 'below';
2418             ret.items_n;
2419             return ret;
2420         }
2421         // could not find a card.. stick it at the end..
2422         if (ret.card_n < 0) {
2423             ret.card_n = last_card_n;
2424             ret.card = ret.cards[last_card_n];
2425             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2426             ret.position = 'below';
2427         }
2428         
2429         if (this.items[ret.items_n].el == dragged_card_el) {
2430             return false;
2431         }
2432         
2433         if (ret.position == 'below') {
2434             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2435             
2436             if (card_after  && card_after.el == dragged_card_el) {
2437                 return false;
2438             }
2439             return ret;
2440         }
2441         
2442         // its's after ..
2443         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2444         
2445         if (card_before  && card_before.el == dragged_card_el) {
2446             return false;
2447         }
2448         
2449         return ret;
2450     },
2451     
2452     onNodeEnter : function(n, dd, e, data){
2453         return false;
2454     },
2455     onNodeOver : function(n, dd, e, data)
2456     {
2457        
2458         var target_info = this.getTargetFromEvent(e,data.source.el);
2459         if (target_info === false) {
2460             this.dropPlaceHolder('hide');
2461             return false;
2462         }
2463         Roo.log(['getTargetFromEvent', target_info ]);
2464         
2465          
2466         this.dropPlaceHolder('show', target_info,data);
2467         
2468         return false; 
2469     },
2470     onNodeOut : function(n, dd, e, data){
2471         this.dropPlaceHolder('hide');
2472      
2473     },
2474     onNodeDrop : function(n, dd, e, data)
2475     {
2476         
2477         // call drop - return false if
2478         
2479         // this could actually fail - if the Network drops..
2480         // we will ignore this at present..- client should probably reload
2481         // the whole set of cards if stuff like that fails.
2482         
2483         
2484         var info = this.getTargetFromEvent(e,data.source.el);
2485         if (info === false) {
2486             return false;
2487         }
2488         this.dropPlaceHolder('hide');
2489   
2490          
2491     
2492     
2493     
2494         this.acceptCard(data.source, info.position, info.card, info.items_n);
2495         return true;
2496          
2497     },
2498     firstChildCard : function()
2499     {
2500         for (var i = 0;i< this.items.length;i++) {
2501             
2502             if (!this.items[i].el.hasClass('card')) {
2503                  continue;
2504             }
2505             return this.items[i];
2506         }
2507         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2508     },
2509     /**
2510      * accept card
2511      *
2512      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2513      */
2514     acceptCard : function(move_card,  position, next_to_card )
2515     {
2516         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2517             return false;
2518         }
2519         
2520         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2521         
2522         move_card.parent().removeCard(move_card);
2523         
2524         
2525         var dom = move_card.el.dom;
2526         dom.style.width = ''; // clear with - which is set by drag.
2527         
2528         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2529             var cardel = next_to_card.el.dom;
2530             
2531             if (position == 'above' ) {
2532                 cardel.parentNode.insertBefore(dom, cardel);
2533             } else if (cardel.nextSibling) {
2534                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2535             } else {
2536                 cardel.parentNode.append(dom);
2537             }
2538         } else {
2539             // card container???
2540             this.containerEl.dom.append(dom);
2541         }
2542         
2543         //FIXME HANDLE card = true 
2544         
2545         // add this to the correct place in items.
2546         
2547         // remove Card from items.
2548         
2549        
2550         if (this.items.length) {
2551             var nitems = [];
2552             //Roo.log([info.items_n, info.position, this.items.length]);
2553             for (var i =0; i < this.items.length; i++) {
2554                 if (i == to_items_n && position == 'above') {
2555                     nitems.push(move_card);
2556                 }
2557                 nitems.push(this.items[i]);
2558                 if (i == to_items_n && position == 'below') {
2559                     nitems.push(move_card);
2560                 }
2561             }
2562             this.items = nitems;
2563             Roo.log(this.items);
2564         } else {
2565             this.items.push(move_card);
2566         }
2567         
2568         move_card.parentId = this.id;
2569         
2570         return true;
2571         
2572         
2573     },
2574     removeCard : function(c)
2575     {
2576         this.items = this.items.filter(function(e) { return e != c });
2577  
2578         var dom = c.el.dom;
2579         dom.parentNode.removeChild(dom);
2580         dom.style.width = ''; // clear with - which is set by drag.
2581         c.parentId = false;
2582         
2583     },
2584     
2585     /**    Decide whether to drop above or below a View node. */
2586     getDropPoint : function(e, n, dd)
2587     {
2588         if (dd) {
2589              return false;
2590         }
2591         if (n == this.containerEl.dom) {
2592             return "above";
2593         }
2594         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2595         var c = t + (b - t) / 2;
2596         var y = Roo.lib.Event.getPageY(e);
2597         if(y <= c) {
2598             return "above";
2599         }else{
2600             return "below";
2601         }
2602     },
2603     onToggleCollapse : function(e)
2604         {
2605         if (this.collapsed) {
2606             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2607             this.collapsableEl.addClass('show');
2608             this.collapsed = false;
2609             return;
2610         }
2611         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2612         this.collapsableEl.removeClass('show');
2613         this.collapsed = true;
2614         
2615     
2616     },
2617     
2618     onToggleRotate : function(e)
2619     {
2620         this.collapsableEl.removeClass('show');
2621         this.footerEl.removeClass('d-none');
2622         this.el.removeClass('roo-card-rotated');
2623         this.el.removeClass('d-none');
2624         if (this.rotated) {
2625             
2626             this.collapsableEl.addClass('show');
2627             this.rotated = false;
2628             this.fireEvent('rotate', this, this.rotated);
2629             return;
2630         }
2631         this.el.addClass('roo-card-rotated');
2632         this.footerEl.addClass('d-none');
2633         this.el.select('.roo-collapsable').removeClass('show');
2634         
2635         this.rotated = true;
2636         this.fireEvent('rotate', this, this.rotated);
2637     
2638     },
2639     
2640     dropPlaceHolder: function (action, info, data)
2641     {
2642         if (this.dropEl === false) {
2643             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2644             cls : 'd-none'
2645             },true);
2646         }
2647         this.dropEl.removeClass(['d-none', 'd-block']);        
2648         if (action == 'hide') {
2649             
2650             this.dropEl.addClass('d-none');
2651             return;
2652         }
2653         // FIXME - info.card == true!!!
2654         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2655         
2656         if (info.card !== true) {
2657             var cardel = info.card.el.dom;
2658             
2659             if (info.position == 'above') {
2660                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2661             } else if (cardel.nextSibling) {
2662                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2663             } else {
2664                 cardel.parentNode.append(this.dropEl.dom);
2665             }
2666         } else {
2667             // card container???
2668             this.containerEl.dom.append(this.dropEl.dom);
2669         }
2670         
2671         this.dropEl.addClass('d-block roo-card-dropzone');
2672         
2673         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2674         
2675         
2676     
2677     
2678     
2679     },
2680     setHeaderText: function(html)
2681     {
2682         this.headerContainerEl.dom.innerHTML = html;
2683     }
2684
2685     
2686 });
2687
2688 /*
2689  * - LGPL
2690  *
2691  * Card header - holder for the card header elements.
2692  * 
2693  */
2694
2695 /**
2696  * @class Roo.bootstrap.CardHeader
2697  * @extends Roo.bootstrap.Element
2698  * Bootstrap CardHeader class
2699  * @constructor
2700  * Create a new Card Header - that you can embed children into
2701  * @param {Object} config The config object
2702  */
2703
2704 Roo.bootstrap.CardHeader = function(config){
2705     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2706 };
2707
2708 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2709     
2710     
2711     container_method : 'getCardHeader' 
2712     
2713      
2714     
2715     
2716    
2717 });
2718
2719  
2720
2721  /*
2722  * - LGPL
2723  *
2724  * Card footer - holder for the card footer elements.
2725  * 
2726  */
2727
2728 /**
2729  * @class Roo.bootstrap.CardFooter
2730  * @extends Roo.bootstrap.Element
2731  * Bootstrap CardFooter class
2732  * @constructor
2733  * Create a new Card Footer - that you can embed children into
2734  * @param {Object} config The config object
2735  */
2736
2737 Roo.bootstrap.CardFooter = function(config){
2738     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2739 };
2740
2741 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2742     
2743     
2744     container_method : 'getCardFooter' 
2745     
2746      
2747     
2748     
2749    
2750 });
2751
2752  
2753
2754  /*
2755  * - LGPL
2756  *
2757  * Card header - holder for the card header elements.
2758  * 
2759  */
2760
2761 /**
2762  * @class Roo.bootstrap.CardImageTop
2763  * @extends Roo.bootstrap.Element
2764  * Bootstrap CardImageTop class
2765  * @constructor
2766  * Create a new Card Image Top container
2767  * @param {Object} config The config object
2768  */
2769
2770 Roo.bootstrap.CardImageTop = function(config){
2771     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2772 };
2773
2774 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2775     
2776    
2777     container_method : 'getCardImageTop' 
2778     
2779      
2780     
2781    
2782 });
2783
2784  
2785
2786  /*
2787  * - LGPL
2788  *
2789  * image
2790  * 
2791  */
2792
2793
2794 /**
2795  * @class Roo.bootstrap.Img
2796  * @extends Roo.bootstrap.Component
2797  * Bootstrap Img class
2798  * @cfg {Boolean} imgResponsive false | true
2799  * @cfg {String} border rounded | circle | thumbnail
2800  * @cfg {String} src image source
2801  * @cfg {String} alt image alternative text
2802  * @cfg {String} href a tag href
2803  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2804  * @cfg {String} xsUrl xs image source
2805  * @cfg {String} smUrl sm image source
2806  * @cfg {String} mdUrl md image source
2807  * @cfg {String} lgUrl lg image source
2808  * 
2809  * @constructor
2810  * Create a new Input
2811  * @param {Object} config The config object
2812  */
2813
2814 Roo.bootstrap.Img = function(config){
2815     Roo.bootstrap.Img.superclass.constructor.call(this, config);
2816     
2817     this.addEvents({
2818         // img events
2819         /**
2820          * @event click
2821          * The img click event for the img.
2822          * @param {Roo.EventObject} e
2823          */
2824         "click" : true
2825     });
2826 };
2827
2828 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
2829     
2830     imgResponsive: true,
2831     border: '',
2832     src: 'about:blank',
2833     href: false,
2834     target: false,
2835     xsUrl: '',
2836     smUrl: '',
2837     mdUrl: '',
2838     lgUrl: '',
2839
2840     getAutoCreate : function()
2841     {   
2842         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2843             return this.createSingleImg();
2844         }
2845         
2846         var cfg = {
2847             tag: 'div',
2848             cls: 'roo-image-responsive-group',
2849             cn: []
2850         };
2851         var _this = this;
2852         
2853         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2854             
2855             if(!_this[size + 'Url']){
2856                 return;
2857             }
2858             
2859             var img = {
2860                 tag: 'img',
2861                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2862                 html: _this.html || cfg.html,
2863                 src: _this[size + 'Url']
2864             };
2865             
2866             img.cls += ' roo-image-responsive-' + size;
2867             
2868             var s = ['xs', 'sm', 'md', 'lg'];
2869             
2870             s.splice(s.indexOf(size), 1);
2871             
2872             Roo.each(s, function(ss){
2873                 img.cls += ' hidden-' + ss;
2874             });
2875             
2876             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2877                 cfg.cls += ' img-' + _this.border;
2878             }
2879             
2880             if(_this.alt){
2881                 cfg.alt = _this.alt;
2882             }
2883             
2884             if(_this.href){
2885                 var a = {
2886                     tag: 'a',
2887                     href: _this.href,
2888                     cn: [
2889                         img
2890                     ]
2891                 };
2892
2893                 if(this.target){
2894                     a.target = _this.target;
2895                 }
2896             }
2897             
2898             cfg.cn.push((_this.href) ? a : img);
2899             
2900         });
2901         
2902         return cfg;
2903     },
2904     
2905     createSingleImg : function()
2906     {
2907         var cfg = {
2908             tag: 'img',
2909             cls: (this.imgResponsive) ? 'img-responsive' : '',
2910             html : null,
2911             src : 'about:blank'  // just incase src get's set to undefined?!?
2912         };
2913         
2914         cfg.html = this.html || cfg.html;
2915         
2916         cfg.src = this.src || cfg.src;
2917         
2918         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2919             cfg.cls += ' img-' + this.border;
2920         }
2921         
2922         if(this.alt){
2923             cfg.alt = this.alt;
2924         }
2925         
2926         if(this.href){
2927             var a = {
2928                 tag: 'a',
2929                 href: this.href,
2930                 cn: [
2931                     cfg
2932                 ]
2933             };
2934             
2935             if(this.target){
2936                 a.target = this.target;
2937             }
2938             
2939         }
2940         
2941         return (this.href) ? a : cfg;
2942     },
2943     
2944     initEvents: function() 
2945     {
2946         if(!this.href){
2947             this.el.on('click', this.onClick, this);
2948         }
2949         
2950     },
2951     
2952     onClick : function(e)
2953     {
2954         Roo.log('img onclick');
2955         this.fireEvent('click', this, e);
2956     },
2957     /**
2958      * Sets the url of the image - used to update it
2959      * @param {String} url the url of the image
2960      */
2961     
2962     setSrc : function(url)
2963     {
2964         this.src =  url;
2965         
2966         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2967             this.el.dom.src =  url;
2968             return;
2969         }
2970         
2971         this.el.select('img', true).first().dom.src =  url;
2972     }
2973     
2974     
2975    
2976 });
2977
2978  /*
2979  * - LGPL
2980  *
2981  * image
2982  * 
2983  */
2984
2985
2986 /**
2987  * @class Roo.bootstrap.Link
2988  * @extends Roo.bootstrap.Component
2989  * Bootstrap Link Class
2990  * @cfg {String} alt image alternative text
2991  * @cfg {String} href a tag href
2992  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2993  * @cfg {String} html the content of the link.
2994  * @cfg {String} anchor name for the anchor link
2995  * @cfg {String} fa - favicon
2996
2997  * @cfg {Boolean} preventDefault (true | false) default false
2998
2999  * 
3000  * @constructor
3001  * Create a new Input
3002  * @param {Object} config The config object
3003  */
3004
3005 Roo.bootstrap.Link = function(config){
3006     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3007     
3008     this.addEvents({
3009         // img events
3010         /**
3011          * @event click
3012          * The img click event for the img.
3013          * @param {Roo.EventObject} e
3014          */
3015         "click" : true
3016     });
3017 };
3018
3019 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3020     
3021     href: false,
3022     target: false,
3023     preventDefault: false,
3024     anchor : false,
3025     alt : false,
3026     fa: false,
3027
3028
3029     getAutoCreate : function()
3030     {
3031         var html = this.html || '';
3032         
3033         if (this.fa !== false) {
3034             html = '<i class="fa fa-' + this.fa + '"></i>';
3035         }
3036         var cfg = {
3037             tag: 'a'
3038         };
3039         // anchor's do not require html/href...
3040         if (this.anchor === false) {
3041             cfg.html = html;
3042             cfg.href = this.href || '#';
3043         } else {
3044             cfg.name = this.anchor;
3045             if (this.html !== false || this.fa !== false) {
3046                 cfg.html = html;
3047             }
3048             if (this.href !== false) {
3049                 cfg.href = this.href;
3050             }
3051         }
3052         
3053         if(this.alt !== false){
3054             cfg.alt = this.alt;
3055         }
3056         
3057         
3058         if(this.target !== false) {
3059             cfg.target = this.target;
3060         }
3061         
3062         return cfg;
3063     },
3064     
3065     initEvents: function() {
3066         
3067         if(!this.href || this.preventDefault){
3068             this.el.on('click', this.onClick, this);
3069         }
3070     },
3071     
3072     onClick : function(e)
3073     {
3074         if(this.preventDefault){
3075             e.preventDefault();
3076         }
3077         //Roo.log('img onclick');
3078         this.fireEvent('click', this, e);
3079     }
3080    
3081 });
3082
3083  /*
3084  * - LGPL
3085  *
3086  * header
3087  * 
3088  */
3089
3090 /**
3091  * @class Roo.bootstrap.Header
3092  * @extends Roo.bootstrap.Component
3093  * Bootstrap Header class
3094  * @cfg {String} html content of header
3095  * @cfg {Number} level (1|2|3|4|5|6) default 1
3096  * 
3097  * @constructor
3098  * Create a new Header
3099  * @param {Object} config The config object
3100  */
3101
3102
3103 Roo.bootstrap.Header  = function(config){
3104     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3105 };
3106
3107 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3108     
3109     //href : false,
3110     html : false,
3111     level : 1,
3112     
3113     
3114     
3115     getAutoCreate : function(){
3116         
3117         
3118         
3119         var cfg = {
3120             tag: 'h' + (1 *this.level),
3121             html: this.html || ''
3122         } ;
3123         
3124         return cfg;
3125     }
3126    
3127 });
3128
3129  
3130
3131  /*
3132  * Based on:
3133  * Ext JS Library 1.1.1
3134  * Copyright(c) 2006-2007, Ext JS, LLC.
3135  *
3136  * Originally Released Under LGPL - original licence link has changed is not relivant.
3137  *
3138  * Fork - LGPL
3139  * <script type="text/javascript">
3140  */
3141  
3142 /**
3143  * @class Roo.bootstrap.MenuMgr
3144  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3145  * @singleton
3146  */
3147 Roo.bootstrap.MenuMgr = function(){
3148    var menus, active, groups = {}, attached = false, lastShow = new Date();
3149
3150    // private - called when first menu is created
3151    function init(){
3152        menus = {};
3153        active = new Roo.util.MixedCollection();
3154        Roo.get(document).addKeyListener(27, function(){
3155            if(active.length > 0){
3156                hideAll();
3157            }
3158        });
3159    }
3160
3161    // private
3162    function hideAll(){
3163        if(active && active.length > 0){
3164            var c = active.clone();
3165            c.each(function(m){
3166                m.hide();
3167            });
3168        }
3169    }
3170
3171    // private
3172    function onHide(m){
3173        active.remove(m);
3174        if(active.length < 1){
3175            Roo.get(document).un("mouseup", onMouseDown);
3176             
3177            attached = false;
3178        }
3179    }
3180
3181    // private
3182    function onShow(m){
3183        var last = active.last();
3184        lastShow = new Date();
3185        active.add(m);
3186        if(!attached){
3187           Roo.get(document).on("mouseup", onMouseDown);
3188            
3189            attached = true;
3190        }
3191        if(m.parentMenu){
3192           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3193           m.parentMenu.activeChild = m;
3194        }else if(last && last.isVisible()){
3195           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3196        }
3197    }
3198
3199    // private
3200    function onBeforeHide(m){
3201        if(m.activeChild){
3202            m.activeChild.hide();
3203        }
3204        if(m.autoHideTimer){
3205            clearTimeout(m.autoHideTimer);
3206            delete m.autoHideTimer;
3207        }
3208    }
3209
3210    // private
3211    function onBeforeShow(m){
3212        var pm = m.parentMenu;
3213        if(!pm && !m.allowOtherMenus){
3214            hideAll();
3215        }else if(pm && pm.activeChild && active != m){
3216            pm.activeChild.hide();
3217        }
3218    }
3219
3220    // private this should really trigger on mouseup..
3221    function onMouseDown(e){
3222         Roo.log("on Mouse Up");
3223         
3224         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3225             Roo.log("MenuManager hideAll");
3226             hideAll();
3227             e.stopEvent();
3228         }
3229         
3230         
3231    }
3232
3233    // private
3234    function onBeforeCheck(mi, state){
3235        if(state){
3236            var g = groups[mi.group];
3237            for(var i = 0, l = g.length; i < l; i++){
3238                if(g[i] != mi){
3239                    g[i].setChecked(false);
3240                }
3241            }
3242        }
3243    }
3244
3245    return {
3246
3247        /**
3248         * Hides all menus that are currently visible
3249         */
3250        hideAll : function(){
3251             hideAll();  
3252        },
3253
3254        // private
3255        register : function(menu){
3256            if(!menus){
3257                init();
3258            }
3259            menus[menu.id] = menu;
3260            menu.on("beforehide", onBeforeHide);
3261            menu.on("hide", onHide);
3262            menu.on("beforeshow", onBeforeShow);
3263            menu.on("show", onShow);
3264            var g = menu.group;
3265            if(g && menu.events["checkchange"]){
3266                if(!groups[g]){
3267                    groups[g] = [];
3268                }
3269                groups[g].push(menu);
3270                menu.on("checkchange", onCheck);
3271            }
3272        },
3273
3274         /**
3275          * Returns a {@link Roo.menu.Menu} object
3276          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3277          * be used to generate and return a new Menu instance.
3278          */
3279        get : function(menu){
3280            if(typeof menu == "string"){ // menu id
3281                return menus[menu];
3282            }else if(menu.events){  // menu instance
3283                return menu;
3284            }
3285            /*else if(typeof menu.length == 'number'){ // array of menu items?
3286                return new Roo.bootstrap.Menu({items:menu});
3287            }else{ // otherwise, must be a config
3288                return new Roo.bootstrap.Menu(menu);
3289            }
3290            */
3291            return false;
3292        },
3293
3294        // private
3295        unregister : function(menu){
3296            delete menus[menu.id];
3297            menu.un("beforehide", onBeforeHide);
3298            menu.un("hide", onHide);
3299            menu.un("beforeshow", onBeforeShow);
3300            menu.un("show", onShow);
3301            var g = menu.group;
3302            if(g && menu.events["checkchange"]){
3303                groups[g].remove(menu);
3304                menu.un("checkchange", onCheck);
3305            }
3306        },
3307
3308        // private
3309        registerCheckable : function(menuItem){
3310            var g = menuItem.group;
3311            if(g){
3312                if(!groups[g]){
3313                    groups[g] = [];
3314                }
3315                groups[g].push(menuItem);
3316                menuItem.on("beforecheckchange", onBeforeCheck);
3317            }
3318        },
3319
3320        // private
3321        unregisterCheckable : function(menuItem){
3322            var g = menuItem.group;
3323            if(g){
3324                groups[g].remove(menuItem);
3325                menuItem.un("beforecheckchange", onBeforeCheck);
3326            }
3327        }
3328    };
3329 }();/*
3330  * - LGPL
3331  *
3332  * menu
3333  * 
3334  */
3335
3336 /**
3337  * @class Roo.bootstrap.Menu
3338  * @extends Roo.bootstrap.Component
3339  * Bootstrap Menu class - container for MenuItems
3340  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3341  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3342  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3343  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3344  * 
3345  * @constructor
3346  * Create a new Menu
3347  * @param {Object} config The config object
3348  */
3349
3350
3351 Roo.bootstrap.Menu = function(config){
3352     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3353     if (this.registerMenu && this.type != 'treeview')  {
3354         Roo.bootstrap.MenuMgr.register(this);
3355     }
3356     
3357     
3358     this.addEvents({
3359         /**
3360          * @event beforeshow
3361          * Fires before this menu is displayed (return false to block)
3362          * @param {Roo.menu.Menu} this
3363          */
3364         beforeshow : true,
3365         /**
3366          * @event beforehide
3367          * Fires before this menu is hidden (return false to block)
3368          * @param {Roo.menu.Menu} this
3369          */
3370         beforehide : true,
3371         /**
3372          * @event show
3373          * Fires after this menu is displayed
3374          * @param {Roo.menu.Menu} this
3375          */
3376         show : true,
3377         /**
3378          * @event hide
3379          * Fires after this menu is hidden
3380          * @param {Roo.menu.Menu} this
3381          */
3382         hide : true,
3383         /**
3384          * @event click
3385          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3386          * @param {Roo.menu.Menu} this
3387          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3388          * @param {Roo.EventObject} e
3389          */
3390         click : true,
3391         /**
3392          * @event mouseover
3393          * Fires when the mouse is hovering over this menu
3394          * @param {Roo.menu.Menu} this
3395          * @param {Roo.EventObject} e
3396          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3397          */
3398         mouseover : true,
3399         /**
3400          * @event mouseout
3401          * Fires when the mouse exits this menu
3402          * @param {Roo.menu.Menu} this
3403          * @param {Roo.EventObject} e
3404          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3405          */
3406         mouseout : true,
3407         /**
3408          * @event itemclick
3409          * Fires when a menu item contained in this menu is clicked
3410          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3411          * @param {Roo.EventObject} e
3412          */
3413         itemclick: true
3414     });
3415     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3416 };
3417
3418 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3419     
3420    /// html : false,
3421     //align : '',
3422     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3423     type: false,
3424     /**
3425      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3426      */
3427     registerMenu : true,
3428     
3429     menuItems :false, // stores the menu items..
3430     
3431     hidden:true,
3432         
3433     parentMenu : false,
3434     
3435     stopEvent : true,
3436     
3437     isLink : false,
3438     
3439     getChildContainer : function() {
3440         return this.el;  
3441     },
3442     
3443     getAutoCreate : function(){
3444          
3445         //if (['right'].indexOf(this.align)!==-1) {
3446         //    cfg.cn[1].cls += ' pull-right'
3447         //}
3448         
3449         
3450         var cfg = {
3451             tag : 'ul',
3452             cls : 'dropdown-menu' ,
3453             style : 'z-index:1000'
3454             
3455         };
3456         
3457         if (this.type === 'submenu') {
3458             cfg.cls = 'submenu active';
3459         }
3460         if (this.type === 'treeview') {
3461             cfg.cls = 'treeview-menu';
3462         }
3463         
3464         return cfg;
3465     },
3466     initEvents : function() {
3467         
3468        // Roo.log("ADD event");
3469        // Roo.log(this.triggerEl.dom);
3470         
3471         this.triggerEl.on('click', this.onTriggerClick, this);
3472         
3473         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3474         
3475         
3476         if (this.triggerEl.hasClass('nav-item')) {
3477             // dropdown toggle on the 'a' in BS4?
3478             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3479         } else {
3480             this.triggerEl.addClass('dropdown-toggle');
3481         }
3482         if (Roo.isTouch) {
3483             this.el.on('touchstart'  , this.onTouch, this);
3484         }
3485         this.el.on('click' , this.onClick, this);
3486
3487         this.el.on("mouseover", this.onMouseOver, this);
3488         this.el.on("mouseout", this.onMouseOut, this);
3489         
3490     },
3491     
3492     findTargetItem : function(e)
3493     {
3494         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3495         if(!t){
3496             return false;
3497         }
3498         //Roo.log(t);         Roo.log(t.id);
3499         if(t && t.id){
3500             //Roo.log(this.menuitems);
3501             return this.menuitems.get(t.id);
3502             
3503             //return this.items.get(t.menuItemId);
3504         }
3505         
3506         return false;
3507     },
3508     
3509     onTouch : function(e) 
3510     {
3511         Roo.log("menu.onTouch");
3512         //e.stopEvent(); this make the user popdown broken
3513         this.onClick(e);
3514     },
3515     
3516     onClick : function(e)
3517     {
3518         Roo.log("menu.onClick");
3519         
3520         var t = this.findTargetItem(e);
3521         if(!t || t.isContainer){
3522             return;
3523         }
3524         Roo.log(e);
3525         /*
3526         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3527             if(t == this.activeItem && t.shouldDeactivate(e)){
3528                 this.activeItem.deactivate();
3529                 delete this.activeItem;
3530                 return;
3531             }
3532             if(t.canActivate){
3533                 this.setActiveItem(t, true);
3534             }
3535             return;
3536             
3537             
3538         }
3539         */
3540        
3541         Roo.log('pass click event');
3542         
3543         t.onClick(e);
3544         
3545         this.fireEvent("click", this, t, e);
3546         
3547         var _this = this;
3548         
3549         if(!t.href.length || t.href == '#'){
3550             (function() { _this.hide(); }).defer(100);
3551         }
3552         
3553     },
3554     
3555     onMouseOver : function(e){
3556         var t  = this.findTargetItem(e);
3557         //Roo.log(t);
3558         //if(t){
3559         //    if(t.canActivate && !t.disabled){
3560         //        this.setActiveItem(t, true);
3561         //    }
3562         //}
3563         
3564         this.fireEvent("mouseover", this, e, t);
3565     },
3566     isVisible : function(){
3567         return !this.hidden;
3568     },
3569     onMouseOut : function(e){
3570         var t  = this.findTargetItem(e);
3571         
3572         //if(t ){
3573         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3574         //        this.activeItem.deactivate();
3575         //        delete this.activeItem;
3576         //    }
3577         //}
3578         this.fireEvent("mouseout", this, e, t);
3579     },
3580     
3581     
3582     /**
3583      * Displays this menu relative to another element
3584      * @param {String/HTMLElement/Roo.Element} element The element to align to
3585      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3586      * the element (defaults to this.defaultAlign)
3587      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3588      */
3589     show : function(el, pos, parentMenu)
3590     {
3591         if (false === this.fireEvent("beforeshow", this)) {
3592             Roo.log("show canceled");
3593             return;
3594         }
3595         this.parentMenu = parentMenu;
3596         if(!this.el){
3597             this.render();
3598         }
3599         
3600         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3601     },
3602      /**
3603      * Displays this menu at a specific xy position
3604      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3605      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3606      */
3607     showAt : function(xy, parentMenu, /* private: */_e){
3608         this.parentMenu = parentMenu;
3609         if(!this.el){
3610             this.render();
3611         }
3612         if(_e !== false){
3613             this.fireEvent("beforeshow", this);
3614             //xy = this.el.adjustForConstraints(xy);
3615         }
3616         
3617         //this.el.show();
3618         this.hideMenuItems();
3619         this.hidden = false;
3620         this.triggerEl.addClass('open');
3621         this.el.addClass('show');
3622         
3623         // reassign x when hitting right
3624         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3625             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3626         }
3627         
3628         // reassign y when hitting bottom
3629         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3630             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3631         }
3632         
3633         // but the list may align on trigger left or trigger top... should it be a properity?
3634         
3635         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3636             this.el.setXY(xy);
3637         }
3638         
3639         this.focus();
3640         this.fireEvent("show", this);
3641     },
3642     
3643     focus : function(){
3644         return;
3645         if(!this.hidden){
3646             this.doFocus.defer(50, this);
3647         }
3648     },
3649
3650     doFocus : function(){
3651         if(!this.hidden){
3652             this.focusEl.focus();
3653         }
3654     },
3655
3656     /**
3657      * Hides this menu and optionally all parent menus
3658      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3659      */
3660     hide : function(deep)
3661     {
3662         if (false === this.fireEvent("beforehide", this)) {
3663             Roo.log("hide canceled");
3664             return;
3665         }
3666         this.hideMenuItems();
3667         if(this.el && this.isVisible()){
3668            
3669             if(this.activeItem){
3670                 this.activeItem.deactivate();
3671                 this.activeItem = null;
3672             }
3673             this.triggerEl.removeClass('open');;
3674             this.el.removeClass('show');
3675             this.hidden = true;
3676             this.fireEvent("hide", this);
3677         }
3678         if(deep === true && this.parentMenu){
3679             this.parentMenu.hide(true);
3680         }
3681     },
3682     
3683     onTriggerClick : function(e)
3684     {
3685         Roo.log('trigger click');
3686         
3687         var target = e.getTarget();
3688         
3689         Roo.log(target.nodeName.toLowerCase());
3690         
3691         if(target.nodeName.toLowerCase() === 'i'){
3692             e.preventDefault();
3693         }
3694         
3695     },
3696     
3697     onTriggerPress  : function(e)
3698     {
3699         Roo.log('trigger press');
3700         //Roo.log(e.getTarget());
3701        // Roo.log(this.triggerEl.dom);
3702        
3703         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3704         var pel = Roo.get(e.getTarget());
3705         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3706             Roo.log('is treeview or dropdown?');
3707             return;
3708         }
3709         
3710         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3711             return;
3712         }
3713         
3714         if (this.isVisible()) {
3715             Roo.log('hide');
3716             this.hide();
3717         } else {
3718             Roo.log('show');
3719             this.show(this.triggerEl, '?', false);
3720         }
3721         
3722         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3723             e.stopEvent();
3724         }
3725         
3726     },
3727        
3728     
3729     hideMenuItems : function()
3730     {
3731         Roo.log("hide Menu Items");
3732         if (!this.el) { 
3733             return;
3734         }
3735         
3736         this.el.select('.open',true).each(function(aa) {
3737             
3738             aa.removeClass('open');
3739          
3740         });
3741     },
3742     addxtypeChild : function (tree, cntr) {
3743         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3744           
3745         this.menuitems.add(comp);
3746         return comp;
3747
3748     },
3749     getEl : function()
3750     {
3751         Roo.log(this.el);
3752         return this.el;
3753     },
3754     
3755     clear : function()
3756     {
3757         this.getEl().dom.innerHTML = '';
3758         this.menuitems.clear();
3759     }
3760 });
3761
3762  
3763  /*
3764  * - LGPL
3765  *
3766  * menu item
3767  * 
3768  */
3769
3770
3771 /**
3772  * @class Roo.bootstrap.MenuItem
3773  * @extends Roo.bootstrap.Component
3774  * Bootstrap MenuItem class
3775  * @cfg {String} html the menu label
3776  * @cfg {String} href the link
3777  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3778  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3779  * @cfg {Boolean} active  used on sidebars to highlight active itesm
3780  * @cfg {String} fa favicon to show on left of menu item.
3781  * @cfg {Roo.bootsrap.Menu} menu the child menu.
3782  * 
3783  * 
3784  * @constructor
3785  * Create a new MenuItem
3786  * @param {Object} config The config object
3787  */
3788
3789
3790 Roo.bootstrap.MenuItem = function(config){
3791     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3792     this.addEvents({
3793         // raw events
3794         /**
3795          * @event click
3796          * The raw click event for the entire grid.
3797          * @param {Roo.bootstrap.MenuItem} this
3798          * @param {Roo.EventObject} e
3799          */
3800         "click" : true
3801     });
3802 };
3803
3804 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
3805     
3806     href : false,
3807     html : false,
3808     preventDefault: false,
3809     isContainer : false,
3810     active : false,
3811     fa: false,
3812     
3813     getAutoCreate : function(){
3814         
3815         if(this.isContainer){
3816             return {
3817                 tag: 'li',
3818                 cls: 'dropdown-menu-item '
3819             };
3820         }
3821         var ctag = {
3822             tag: 'span',
3823             html: 'Link'
3824         };
3825         
3826         var anc = {
3827             tag : 'a',
3828             cls : 'dropdown-item',
3829             href : '#',
3830             cn : [  ]
3831         };
3832         
3833         if (this.fa !== false) {
3834             anc.cn.push({
3835                 tag : 'i',
3836                 cls : 'fa fa-' + this.fa
3837             });
3838         }
3839         
3840         anc.cn.push(ctag);
3841         
3842         
3843         var cfg= {
3844             tag: 'li',
3845             cls: 'dropdown-menu-item',
3846             cn: [ anc ]
3847         };
3848         if (this.parent().type == 'treeview') {
3849             cfg.cls = 'treeview-menu';
3850         }
3851         if (this.active) {
3852             cfg.cls += ' active';
3853         }
3854         
3855         
3856         
3857         anc.href = this.href || cfg.cn[0].href ;
3858         ctag.html = this.html || cfg.cn[0].html ;
3859         return cfg;
3860     },
3861     
3862     initEvents: function()
3863     {
3864         if (this.parent().type == 'treeview') {
3865             this.el.select('a').on('click', this.onClick, this);
3866         }
3867         
3868         if (this.menu) {
3869             this.menu.parentType = this.xtype;
3870             this.menu.triggerEl = this.el;
3871             this.menu = this.addxtype(Roo.apply({}, this.menu));
3872         }
3873         
3874     },
3875     onClick : function(e)
3876     {
3877         Roo.log('item on click ');
3878         
3879         if(this.preventDefault){
3880             e.preventDefault();
3881         }
3882         //this.parent().hideMenuItems();
3883         
3884         this.fireEvent('click', this, e);
3885     },
3886     getEl : function()
3887     {
3888         return this.el;
3889     } 
3890 });
3891
3892  
3893
3894  /*
3895  * - LGPL
3896  *
3897  * menu separator
3898  * 
3899  */
3900
3901
3902 /**
3903  * @class Roo.bootstrap.MenuSeparator
3904  * @extends Roo.bootstrap.Component
3905  * Bootstrap MenuSeparator class
3906  * 
3907  * @constructor
3908  * Create a new MenuItem
3909  * @param {Object} config The config object
3910  */
3911
3912
3913 Roo.bootstrap.MenuSeparator = function(config){
3914     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3915 };
3916
3917 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
3918     
3919     getAutoCreate : function(){
3920         var cfg = {
3921             cls: 'divider',
3922             tag : 'li'
3923         };
3924         
3925         return cfg;
3926     }
3927    
3928 });
3929
3930  
3931
3932  
3933 /*
3934 * Licence: LGPL
3935 */
3936
3937 /**
3938  * @class Roo.bootstrap.Modal
3939  * @extends Roo.bootstrap.Component
3940  * Bootstrap Modal class
3941  * @cfg {String} title Title of dialog
3942  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3943  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
3944  * @cfg {Boolean} specificTitle default false
3945  * @cfg {Array} buttons Array of buttons or standard button set..
3946  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3947  * @cfg {Boolean} animate default true
3948  * @cfg {Boolean} allow_close default true
3949  * @cfg {Boolean} fitwindow default false
3950  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
3951  * @cfg {Number} width fixed width - usefull for chrome extension only really.
3952  * @cfg {Number} height fixed height - usefull for chrome extension only really.
3953  * @cfg {String} size (sm|lg|xl) default empty
3954  * @cfg {Number} max_width set the max width of modal
3955  * @cfg {Boolean} editableTitle can the title be edited
3956
3957  *
3958  *
3959  * @constructor
3960  * Create a new Modal Dialog
3961  * @param {Object} config The config object
3962  */
3963
3964 Roo.bootstrap.Modal = function(config){
3965     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3966     this.addEvents({
3967         // raw events
3968         /**
3969          * @event btnclick
3970          * The raw btnclick event for the button
3971          * @param {Roo.EventObject} e
3972          */
3973         "btnclick" : true,
3974         /**
3975          * @event resize
3976          * Fire when dialog resize
3977          * @param {Roo.bootstrap.Modal} this
3978          * @param {Roo.EventObject} e
3979          */
3980         "resize" : true,
3981         /**
3982          * @event titlechanged
3983          * Fire when the editable title has been changed
3984          * @param {Roo.bootstrap.Modal} this
3985          * @param {Roo.EventObject} value
3986          */
3987         "titlechanged" : true 
3988         
3989     });
3990     this.buttons = this.buttons || [];
3991
3992     if (this.tmpl) {
3993         this.tmpl = Roo.factory(this.tmpl);
3994     }
3995
3996 };
3997
3998 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
3999
4000     title : 'test dialog',
4001
4002     buttons : false,
4003
4004     // set on load...
4005
4006     html: false,
4007
4008     tmp: false,
4009
4010     specificTitle: false,
4011
4012     buttonPosition: 'right',
4013
4014     allow_close : true,
4015
4016     animate : true,
4017
4018     fitwindow: false,
4019     
4020      // private
4021     dialogEl: false,
4022     bodyEl:  false,
4023     footerEl:  false,
4024     titleEl:  false,
4025     closeEl:  false,
4026
4027     size: '',
4028     
4029     max_width: 0,
4030     
4031     max_height: 0,
4032     
4033     fit_content: false,
4034     editableTitle  : false,
4035
4036     onRender : function(ct, position)
4037     {
4038         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4039
4040         if(!this.el){
4041             var cfg = Roo.apply({},  this.getAutoCreate());
4042             cfg.id = Roo.id();
4043             //if(!cfg.name){
4044             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4045             //}
4046             //if (!cfg.name.length) {
4047             //    delete cfg.name;
4048            // }
4049             if (this.cls) {
4050                 cfg.cls += ' ' + this.cls;
4051             }
4052             if (this.style) {
4053                 cfg.style = this.style;
4054             }
4055             this.el = Roo.get(document.body).createChild(cfg, position);
4056         }
4057         //var type = this.el.dom.type;
4058
4059
4060         if(this.tabIndex !== undefined){
4061             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4062         }
4063
4064         this.dialogEl = this.el.select('.modal-dialog',true).first();
4065         this.bodyEl = this.el.select('.modal-body',true).first();
4066         this.closeEl = this.el.select('.modal-header .close', true).first();
4067         this.headerEl = this.el.select('.modal-header',true).first();
4068         this.titleEl = this.el.select('.modal-title',true).first();
4069         this.footerEl = this.el.select('.modal-footer',true).first();
4070
4071         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4072         
4073         //this.el.addClass("x-dlg-modal");
4074
4075         if (this.buttons.length) {
4076             Roo.each(this.buttons, function(bb) {
4077                 var b = Roo.apply({}, bb);
4078                 b.xns = b.xns || Roo.bootstrap;
4079                 b.xtype = b.xtype || 'Button';
4080                 if (typeof(b.listeners) == 'undefined') {
4081                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4082                 }
4083
4084                 var btn = Roo.factory(b);
4085
4086                 btn.render(this.getButtonContainer());
4087
4088             },this);
4089         }
4090         // render the children.
4091         var nitems = [];
4092
4093         if(typeof(this.items) != 'undefined'){
4094             var items = this.items;
4095             delete this.items;
4096
4097             for(var i =0;i < items.length;i++) {
4098                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4099             }
4100         }
4101
4102         this.items = nitems;
4103
4104         // where are these used - they used to be body/close/footer
4105
4106
4107         this.initEvents();
4108         //this.el.addClass([this.fieldClass, this.cls]);
4109
4110     },
4111
4112     getAutoCreate : function()
4113     {
4114         // we will default to modal-body-overflow - might need to remove or make optional later.
4115         var bdy = {
4116                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4117                 html : this.html || ''
4118         };
4119
4120         var title = {
4121             tag: 'h5',
4122             cls : 'modal-title',
4123             html : this.title
4124         };
4125
4126         if(this.specificTitle){ // WTF is this?
4127             title = this.title;
4128         }
4129
4130         var header = [];
4131         if (this.allow_close && Roo.bootstrap.version == 3) {
4132             header.push({
4133                 tag: 'button',
4134                 cls : 'close',
4135                 html : '&times'
4136             });
4137         }
4138
4139         header.push(title);
4140
4141         if (this.editableTitle) {
4142             header.push({
4143                 cls: 'form-control roo-editable-title d-none',
4144                 tag: 'input',
4145                 type: 'text'
4146             });
4147         }
4148         
4149         if (this.allow_close && Roo.bootstrap.version == 4) {
4150             header.push({
4151                 tag: 'button',
4152                 cls : 'close',
4153                 html : '&times'
4154             });
4155         }
4156         
4157         var size = '';
4158
4159         if(this.size.length){
4160             size = 'modal-' + this.size;
4161         }
4162         
4163         var footer = Roo.bootstrap.version == 3 ?
4164             {
4165                 cls : 'modal-footer',
4166                 cn : [
4167                     {
4168                         tag: 'div',
4169                         cls: 'btn-' + this.buttonPosition
4170                     }
4171                 ]
4172
4173             } :
4174             {  // BS4 uses mr-auto on left buttons....
4175                 cls : 'modal-footer'
4176             };
4177
4178             
4179
4180         
4181         
4182         var modal = {
4183             cls: "modal",
4184              cn : [
4185                 {
4186                     cls: "modal-dialog " + size,
4187                     cn : [
4188                         {
4189                             cls : "modal-content",
4190                             cn : [
4191                                 {
4192                                     cls : 'modal-header',
4193                                     cn : header
4194                                 },
4195                                 bdy,
4196                                 footer
4197                             ]
4198
4199                         }
4200                     ]
4201
4202                 }
4203             ]
4204         };
4205
4206         if(this.animate){
4207             modal.cls += ' fade';
4208         }
4209
4210         return modal;
4211
4212     },
4213     getChildContainer : function() {
4214
4215          return this.bodyEl;
4216
4217     },
4218     getButtonContainer : function() {
4219         
4220          return Roo.bootstrap.version == 4 ?
4221             this.el.select('.modal-footer',true).first()
4222             : this.el.select('.modal-footer div',true).first();
4223
4224     },
4225     initEvents : function()
4226     {
4227         if (this.allow_close) {
4228             this.closeEl.on('click', this.hide, this);
4229         }
4230         Roo.EventManager.onWindowResize(this.resize, this, true);
4231         if (this.editableTitle) {
4232             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4233             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4234             this.headerEditEl.on('keyup', function(e) {
4235                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4236                         this.toggleHeaderInput(false)
4237                     }
4238                 }, this);
4239             this.headerEditEl.on('blur', function(e) {
4240                 this.toggleHeaderInput(false)
4241             },this);
4242         }
4243
4244     },
4245   
4246
4247     resize : function()
4248     {
4249         this.maskEl.setSize(
4250             Roo.lib.Dom.getViewWidth(true),
4251             Roo.lib.Dom.getViewHeight(true)
4252         );
4253         
4254         if (this.fitwindow) {
4255             
4256            this.dialogEl.setStyle( { 'max-width' : '100%' });
4257             this.setSize(
4258                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4259                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4260             );
4261             return;
4262         }
4263         
4264         if(this.max_width !== 0) {
4265             
4266             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4267             
4268             if(this.height) {
4269                 this.setSize(w, this.height);
4270                 return;
4271             }
4272             
4273             if(this.max_height) {
4274                 this.setSize(w,Math.min(
4275                     this.max_height,
4276                     Roo.lib.Dom.getViewportHeight(true) - 60
4277                 ));
4278                 
4279                 return;
4280             }
4281             
4282             if(!this.fit_content) {
4283                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4284                 return;
4285             }
4286             
4287             this.setSize(w, Math.min(
4288                 60 +
4289                 this.headerEl.getHeight() + 
4290                 this.footerEl.getHeight() + 
4291                 this.getChildHeight(this.bodyEl.dom.childNodes),
4292                 Roo.lib.Dom.getViewportHeight(true) - 60)
4293             );
4294         }
4295         
4296     },
4297
4298     setSize : function(w,h)
4299     {
4300         if (!w && !h) {
4301             return;
4302         }
4303         
4304         this.resizeTo(w,h);
4305     },
4306
4307     show : function() {
4308
4309         if (!this.rendered) {
4310             this.render();
4311         }
4312         this.toggleHeaderInput(false);
4313         //this.el.setStyle('display', 'block');
4314         this.el.removeClass('hideing');
4315         this.el.dom.style.display='block';
4316         
4317         Roo.get(document.body).addClass('modal-open');
4318  
4319         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4320             
4321             (function(){
4322                 this.el.addClass('show');
4323                 this.el.addClass('in');
4324             }).defer(50, this);
4325         }else{
4326             this.el.addClass('show');
4327             this.el.addClass('in');
4328         }
4329
4330         // not sure how we can show data in here..
4331         //if (this.tmpl) {
4332         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4333         //}
4334
4335         Roo.get(document.body).addClass("x-body-masked");
4336         
4337         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4338         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4339         this.maskEl.dom.style.display = 'block';
4340         this.maskEl.addClass('show');
4341         
4342         
4343         this.resize();
4344         
4345         this.fireEvent('show', this);
4346
4347         // set zindex here - otherwise it appears to be ignored...
4348         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4349
4350         (function () {
4351             this.items.forEach( function(e) {
4352                 e.layout ? e.layout() : false;
4353
4354             });
4355         }).defer(100,this);
4356
4357     },
4358     hide : function()
4359     {
4360         if(this.fireEvent("beforehide", this) !== false){
4361             
4362             this.maskEl.removeClass('show');
4363             
4364             this.maskEl.dom.style.display = '';
4365             Roo.get(document.body).removeClass("x-body-masked");
4366             this.el.removeClass('in');
4367             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4368
4369             if(this.animate){ // why
4370                 this.el.addClass('hideing');
4371                 this.el.removeClass('show');
4372                 (function(){
4373                     if (!this.el.hasClass('hideing')) {
4374                         return; // it's been shown again...
4375                     }
4376                     
4377                     this.el.dom.style.display='';
4378
4379                     Roo.get(document.body).removeClass('modal-open');
4380                     this.el.removeClass('hideing');
4381                 }).defer(150,this);
4382                 
4383             }else{
4384                 this.el.removeClass('show');
4385                 this.el.dom.style.display='';
4386                 Roo.get(document.body).removeClass('modal-open');
4387
4388             }
4389             this.fireEvent('hide', this);
4390         }
4391     },
4392     isVisible : function()
4393     {
4394         
4395         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4396         
4397     },
4398
4399     addButton : function(str, cb)
4400     {
4401
4402
4403         var b = Roo.apply({}, { html : str } );
4404         b.xns = b.xns || Roo.bootstrap;
4405         b.xtype = b.xtype || 'Button';
4406         if (typeof(b.listeners) == 'undefined') {
4407             b.listeners = { click : cb.createDelegate(this)  };
4408         }
4409
4410         var btn = Roo.factory(b);
4411
4412         btn.render(this.getButtonContainer());
4413
4414         return btn;
4415
4416     },
4417
4418     setDefaultButton : function(btn)
4419     {
4420         //this.el.select('.modal-footer').()
4421     },
4422
4423     resizeTo: function(w,h)
4424     {
4425         this.dialogEl.setWidth(w);
4426         
4427         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4428
4429         this.bodyEl.setHeight(h - diff);
4430         
4431         this.fireEvent('resize', this);
4432     },
4433     
4434     setContentSize  : function(w, h)
4435     {
4436
4437     },
4438     onButtonClick: function(btn,e)
4439     {
4440         //Roo.log([a,b,c]);
4441         this.fireEvent('btnclick', btn.name, e);
4442     },
4443      /**
4444      * Set the title of the Dialog
4445      * @param {String} str new Title
4446      */
4447     setTitle: function(str) {
4448         this.titleEl.dom.innerHTML = str;
4449         this.title = str;
4450     },
4451     /**
4452      * Set the body of the Dialog
4453      * @param {String} str new Title
4454      */
4455     setBody: function(str) {
4456         this.bodyEl.dom.innerHTML = str;
4457     },
4458     /**
4459      * Set the body of the Dialog using the template
4460      * @param {Obj} data - apply this data to the template and replace the body contents.
4461      */
4462     applyBody: function(obj)
4463     {
4464         if (!this.tmpl) {
4465             Roo.log("Error - using apply Body without a template");
4466             //code
4467         }
4468         this.tmpl.overwrite(this.bodyEl, obj);
4469     },
4470     
4471     getChildHeight : function(child_nodes)
4472     {
4473         if(
4474             !child_nodes ||
4475             child_nodes.length == 0
4476         ) {
4477             return 0;
4478         }
4479         
4480         var child_height = 0;
4481         
4482         for(var i = 0; i < child_nodes.length; i++) {
4483             
4484             /*
4485             * for modal with tabs...
4486             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4487                 
4488                 var layout_childs = child_nodes[i].childNodes;
4489                 
4490                 for(var j = 0; j < layout_childs.length; j++) {
4491                     
4492                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4493                         
4494                         var layout_body_childs = layout_childs[j].childNodes;
4495                         
4496                         for(var k = 0; k < layout_body_childs.length; k++) {
4497                             
4498                             if(layout_body_childs[k].classList.contains('navbar')) {
4499                                 child_height += layout_body_childs[k].offsetHeight;
4500                                 continue;
4501                             }
4502                             
4503                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4504                                 
4505                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4506                                 
4507                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4508                                     
4509                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4510                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4511                                         continue;
4512                                     }
4513                                     
4514                                 }
4515                                 
4516                             }
4517                             
4518                         }
4519                     }
4520                 }
4521                 continue;
4522             }
4523             */
4524             
4525             child_height += child_nodes[i].offsetHeight;
4526             // Roo.log(child_nodes[i].offsetHeight);
4527         }
4528         
4529         return child_height;
4530     },
4531     toggleHeaderInput : function(is_edit)
4532     {
4533         if (!this.editableTitle) {
4534             return; // not editable.
4535         }
4536         if (is_edit && this.is_header_editing) {
4537             return; // already editing..
4538         }
4539         if (is_edit) {
4540     
4541             this.headerEditEl.dom.value = this.title;
4542             this.headerEditEl.removeClass('d-none');
4543             this.headerEditEl.dom.focus();
4544             this.titleEl.addClass('d-none');
4545             
4546             this.is_header_editing = true;
4547             return
4548         }
4549         // flip back to not editing.
4550         this.title = this.headerEditEl.dom.value;
4551         this.headerEditEl.addClass('d-none');
4552         this.titleEl.removeClass('d-none');
4553         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4554         this.is_header_editing = false;
4555         this.fireEvent('titlechanged', this, this.title);
4556     
4557             
4558         
4559     }
4560
4561 });
4562
4563
4564 Roo.apply(Roo.bootstrap.Modal,  {
4565     /**
4566          * Button config that displays a single OK button
4567          * @type Object
4568          */
4569         OK :  [{
4570             name : 'ok',
4571             weight : 'primary',
4572             html : 'OK'
4573         }],
4574         /**
4575          * Button config that displays Yes and No buttons
4576          * @type Object
4577          */
4578         YESNO : [
4579             {
4580                 name  : 'no',
4581                 html : 'No'
4582             },
4583             {
4584                 name  :'yes',
4585                 weight : 'primary',
4586                 html : 'Yes'
4587             }
4588         ],
4589
4590         /**
4591          * Button config that displays OK and Cancel buttons
4592          * @type Object
4593          */
4594         OKCANCEL : [
4595             {
4596                name : 'cancel',
4597                 html : 'Cancel'
4598             },
4599             {
4600                 name : 'ok',
4601                 weight : 'primary',
4602                 html : 'OK'
4603             }
4604         ],
4605         /**
4606          * Button config that displays Yes, No and Cancel buttons
4607          * @type Object
4608          */
4609         YESNOCANCEL : [
4610             {
4611                 name : 'yes',
4612                 weight : 'primary',
4613                 html : 'Yes'
4614             },
4615             {
4616                 name : 'no',
4617                 html : 'No'
4618             },
4619             {
4620                 name : 'cancel',
4621                 html : 'Cancel'
4622             }
4623         ],
4624         
4625         zIndex : 10001
4626 });
4627
4628 /*
4629  * - LGPL
4630  *
4631  * messagebox - can be used as a replace
4632  * 
4633  */
4634 /**
4635  * @class Roo.MessageBox
4636  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4637  * Example usage:
4638  *<pre><code>
4639 // Basic alert:
4640 Roo.Msg.alert('Status', 'Changes saved successfully.');
4641
4642 // Prompt for user data:
4643 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4644     if (btn == 'ok'){
4645         // process text value...
4646     }
4647 });
4648
4649 // Show a dialog using config options:
4650 Roo.Msg.show({
4651    title:'Save Changes?',
4652    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4653    buttons: Roo.Msg.YESNOCANCEL,
4654    fn: processResult,
4655    animEl: 'elId'
4656 });
4657 </code></pre>
4658  * @singleton
4659  */
4660 Roo.bootstrap.MessageBox = function(){
4661     var dlg, opt, mask, waitTimer;
4662     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4663     var buttons, activeTextEl, bwidth;
4664
4665     
4666     // private
4667     var handleButton = function(button){
4668         dlg.hide();
4669         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4670     };
4671
4672     // private
4673     var handleHide = function(){
4674         if(opt && opt.cls){
4675             dlg.el.removeClass(opt.cls);
4676         }
4677         //if(waitTimer){
4678         //    Roo.TaskMgr.stop(waitTimer);
4679         //    waitTimer = null;
4680         //}
4681     };
4682
4683     // private
4684     var updateButtons = function(b){
4685         var width = 0;
4686         if(!b){
4687             buttons["ok"].hide();
4688             buttons["cancel"].hide();
4689             buttons["yes"].hide();
4690             buttons["no"].hide();
4691             dlg.footerEl.hide();
4692             
4693             return width;
4694         }
4695         dlg.footerEl.show();
4696         for(var k in buttons){
4697             if(typeof buttons[k] != "function"){
4698                 if(b[k]){
4699                     buttons[k].show();
4700                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4701                     width += buttons[k].el.getWidth()+15;
4702                 }else{
4703                     buttons[k].hide();
4704                 }
4705             }
4706         }
4707         return width;
4708     };
4709
4710     // private
4711     var handleEsc = function(d, k, e){
4712         if(opt && opt.closable !== false){
4713             dlg.hide();
4714         }
4715         if(e){
4716             e.stopEvent();
4717         }
4718     };
4719
4720     return {
4721         /**
4722          * Returns a reference to the underlying {@link Roo.BasicDialog} element
4723          * @return {Roo.BasicDialog} The BasicDialog element
4724          */
4725         getDialog : function(){
4726            if(!dlg){
4727                 dlg = new Roo.bootstrap.Modal( {
4728                     //draggable: true,
4729                     //resizable:false,
4730                     //constraintoviewport:false,
4731                     //fixedcenter:true,
4732                     //collapsible : false,
4733                     //shim:true,
4734                     //modal: true,
4735                 //    width: 'auto',
4736                   //  height:100,
4737                     //buttonAlign:"center",
4738                     closeClick : function(){
4739                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4740                             handleButton("no");
4741                         }else{
4742                             handleButton("cancel");
4743                         }
4744                     }
4745                 });
4746                 dlg.render();
4747                 dlg.on("hide", handleHide);
4748                 mask = dlg.mask;
4749                 //dlg.addKeyListener(27, handleEsc);
4750                 buttons = {};
4751                 this.buttons = buttons;
4752                 var bt = this.buttonText;
4753                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4754                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4755                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4756                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4757                 //Roo.log(buttons);
4758                 bodyEl = dlg.bodyEl.createChild({
4759
4760                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4761                         '<textarea class="roo-mb-textarea"></textarea>' +
4762                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
4763                 });
4764                 msgEl = bodyEl.dom.firstChild;
4765                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4766                 textboxEl.enableDisplayMode();
4767                 textboxEl.addKeyListener([10,13], function(){
4768                     if(dlg.isVisible() && opt && opt.buttons){
4769                         if(opt.buttons.ok){
4770                             handleButton("ok");
4771                         }else if(opt.buttons.yes){
4772                             handleButton("yes");
4773                         }
4774                     }
4775                 });
4776                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4777                 textareaEl.enableDisplayMode();
4778                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4779                 progressEl.enableDisplayMode();
4780                 
4781                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4782                 var pf = progressEl.dom.firstChild;
4783                 if (pf) {
4784                     pp = Roo.get(pf.firstChild);
4785                     pp.setHeight(pf.offsetHeight);
4786                 }
4787                 
4788             }
4789             return dlg;
4790         },
4791
4792         /**
4793          * Updates the message box body text
4794          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4795          * the XHTML-compliant non-breaking space character '&amp;#160;')
4796          * @return {Roo.MessageBox} This message box
4797          */
4798         updateText : function(text)
4799         {
4800             if(!dlg.isVisible() && !opt.width){
4801                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4802                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4803             }
4804             msgEl.innerHTML = text || '&#160;';
4805       
4806             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4807             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4808             var w = Math.max(
4809                     Math.min(opt.width || cw , this.maxWidth), 
4810                     Math.max(opt.minWidth || this.minWidth, bwidth)
4811             );
4812             if(opt.prompt){
4813                 activeTextEl.setWidth(w);
4814             }
4815             if(dlg.isVisible()){
4816                 dlg.fixedcenter = false;
4817             }
4818             // to big, make it scroll. = But as usual stupid IE does not support
4819             // !important..
4820             
4821             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4822                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4823                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4824             } else {
4825                 bodyEl.dom.style.height = '';
4826                 bodyEl.dom.style.overflowY = '';
4827             }
4828             if (cw > w) {
4829                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4830             } else {
4831                 bodyEl.dom.style.overflowX = '';
4832             }
4833             
4834             dlg.setContentSize(w, bodyEl.getHeight());
4835             if(dlg.isVisible()){
4836                 dlg.fixedcenter = true;
4837             }
4838             return this;
4839         },
4840
4841         /**
4842          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
4843          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4844          * @param {Number} value Any number between 0 and 1 (e.g., .5)
4845          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4846          * @return {Roo.MessageBox} This message box
4847          */
4848         updateProgress : function(value, text){
4849             if(text){
4850                 this.updateText(text);
4851             }
4852             
4853             if (pp) { // weird bug on my firefox - for some reason this is not defined
4854                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4855                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4856             }
4857             return this;
4858         },        
4859
4860         /**
4861          * Returns true if the message box is currently displayed
4862          * @return {Boolean} True if the message box is visible, else false
4863          */
4864         isVisible : function(){
4865             return dlg && dlg.isVisible();  
4866         },
4867
4868         /**
4869          * Hides the message box if it is displayed
4870          */
4871         hide : function(){
4872             if(this.isVisible()){
4873                 dlg.hide();
4874             }  
4875         },
4876
4877         /**
4878          * Displays a new message box, or reinitializes an existing message box, based on the config options
4879          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4880          * The following config object properties are supported:
4881          * <pre>
4882 Property    Type             Description
4883 ----------  ---------------  ------------------------------------------------------------------------------------
4884 animEl            String/Element   An id or Element from which the message box should animate as it opens and
4885                                    closes (defaults to undefined)
4886 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4887                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
4888 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
4889                                    progress and wait dialogs will ignore this property and always hide the
4890                                    close button as they can only be closed programmatically.
4891 cls               String           A custom CSS class to apply to the message box element
4892 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
4893                                    displayed (defaults to 75)
4894 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
4895                                    function will be btn (the name of the button that was clicked, if applicable,
4896                                    e.g. "ok"), and text (the value of the active text field, if applicable).
4897                                    Progress and wait dialogs will ignore this option since they do not respond to
4898                                    user actions and can only be closed programmatically, so any required function
4899                                    should be called by the same code after it closes the dialog.
4900 icon              String           A CSS class that provides a background image to be used as an icon for
4901                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4902 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
4903 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
4904 modal             Boolean          False to allow user interaction with the page while the message box is
4905                                    displayed (defaults to true)
4906 msg               String           A string that will replace the existing message box body text (defaults
4907                                    to the XHTML-compliant non-breaking space character '&#160;')
4908 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
4909 progress          Boolean          True to display a progress bar (defaults to false)
4910 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
4911 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
4912 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
4913 title             String           The title text
4914 value             String           The string value to set into the active textbox element if displayed
4915 wait              Boolean          True to display a progress bar (defaults to false)
4916 width             Number           The width of the dialog in pixels
4917 </pre>
4918          *
4919          * Example usage:
4920          * <pre><code>
4921 Roo.Msg.show({
4922    title: 'Address',
4923    msg: 'Please enter your address:',
4924    width: 300,
4925    buttons: Roo.MessageBox.OKCANCEL,
4926    multiline: true,
4927    fn: saveAddress,
4928    animEl: 'addAddressBtn'
4929 });
4930 </code></pre>
4931          * @param {Object} config Configuration options
4932          * @return {Roo.MessageBox} This message box
4933          */
4934         show : function(options)
4935         {
4936             
4937             // this causes nightmares if you show one dialog after another
4938             // especially on callbacks..
4939              
4940             if(this.isVisible()){
4941                 
4942                 this.hide();
4943                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4944                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
4945                 Roo.log("New Dialog Message:" +  options.msg )
4946                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4947                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4948                 
4949             }
4950             var d = this.getDialog();
4951             opt = options;
4952             d.setTitle(opt.title || "&#160;");
4953             d.closeEl.setDisplayed(opt.closable !== false);
4954             activeTextEl = textboxEl;
4955             opt.prompt = opt.prompt || (opt.multiline ? true : false);
4956             if(opt.prompt){
4957                 if(opt.multiline){
4958                     textboxEl.hide();
4959                     textareaEl.show();
4960                     textareaEl.setHeight(typeof opt.multiline == "number" ?
4961                         opt.multiline : this.defaultTextHeight);
4962                     activeTextEl = textareaEl;
4963                 }else{
4964                     textboxEl.show();
4965                     textareaEl.hide();
4966                 }
4967             }else{
4968                 textboxEl.hide();
4969                 textareaEl.hide();
4970             }
4971             progressEl.setDisplayed(opt.progress === true);
4972             if (opt.progress) {
4973                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4974             }
4975             this.updateProgress(0);
4976             activeTextEl.dom.value = opt.value || "";
4977             if(opt.prompt){
4978                 dlg.setDefaultButton(activeTextEl);
4979             }else{
4980                 var bs = opt.buttons;
4981                 var db = null;
4982                 if(bs && bs.ok){
4983                     db = buttons["ok"];
4984                 }else if(bs && bs.yes){
4985                     db = buttons["yes"];
4986                 }
4987                 dlg.setDefaultButton(db);
4988             }
4989             bwidth = updateButtons(opt.buttons);
4990             this.updateText(opt.msg);
4991             if(opt.cls){
4992                 d.el.addClass(opt.cls);
4993             }
4994             d.proxyDrag = opt.proxyDrag === true;
4995             d.modal = opt.modal !== false;
4996             d.mask = opt.modal !== false ? mask : false;
4997             if(!d.isVisible()){
4998                 // force it to the end of the z-index stack so it gets a cursor in FF
4999                 document.body.appendChild(dlg.el.dom);
5000                 d.animateTarget = null;
5001                 d.show(options.animEl);
5002             }
5003             return this;
5004         },
5005
5006         /**
5007          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5008          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5009          * and closing the message box when the process is complete.
5010          * @param {String} title The title bar text
5011          * @param {String} msg The message box body text
5012          * @return {Roo.MessageBox} This message box
5013          */
5014         progress : function(title, msg){
5015             this.show({
5016                 title : title,
5017                 msg : msg,
5018                 buttons: false,
5019                 progress:true,
5020                 closable:false,
5021                 minWidth: this.minProgressWidth,
5022                 modal : true
5023             });
5024             return this;
5025         },
5026
5027         /**
5028          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5029          * If a callback function is passed it will be called after the user clicks the button, and the
5030          * id of the button that was clicked will be passed as the only parameter to the callback
5031          * (could also be the top-right close button).
5032          * @param {String} title The title bar text
5033          * @param {String} msg The message box body text
5034          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5035          * @param {Object} scope (optional) The scope of the callback function
5036          * @return {Roo.MessageBox} This message box
5037          */
5038         alert : function(title, msg, fn, scope)
5039         {
5040             this.show({
5041                 title : title,
5042                 msg : msg,
5043                 buttons: this.OK,
5044                 fn: fn,
5045                 closable : false,
5046                 scope : scope,
5047                 modal : true
5048             });
5049             return this;
5050         },
5051
5052         /**
5053          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5054          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5055          * You are responsible for closing the message box when the process is complete.
5056          * @param {String} msg The message box body text
5057          * @param {String} title (optional) The title bar text
5058          * @return {Roo.MessageBox} This message box
5059          */
5060         wait : function(msg, title){
5061             this.show({
5062                 title : title,
5063                 msg : msg,
5064                 buttons: false,
5065                 closable:false,
5066                 progress:true,
5067                 modal:true,
5068                 width:300,
5069                 wait:true
5070             });
5071             waitTimer = Roo.TaskMgr.start({
5072                 run: function(i){
5073                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5074                 },
5075                 interval: 1000
5076             });
5077             return this;
5078         },
5079
5080         /**
5081          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5082          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5083          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5084          * @param {String} title The title bar text
5085          * @param {String} msg The message box body text
5086          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5087          * @param {Object} scope (optional) The scope of the callback function
5088          * @return {Roo.MessageBox} This message box
5089          */
5090         confirm : function(title, msg, fn, scope){
5091             this.show({
5092                 title : title,
5093                 msg : msg,
5094                 buttons: this.YESNO,
5095                 fn: fn,
5096                 scope : scope,
5097                 modal : true
5098             });
5099             return this;
5100         },
5101
5102         /**
5103          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5104          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5105          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5106          * (could also be the top-right close button) and the text that was entered will be passed as the two
5107          * parameters to the callback.
5108          * @param {String} title The title bar text
5109          * @param {String} msg The message box body text
5110          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5111          * @param {Object} scope (optional) The scope of the callback function
5112          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5113          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5114          * @return {Roo.MessageBox} This message box
5115          */
5116         prompt : function(title, msg, fn, scope, multiline){
5117             this.show({
5118                 title : title,
5119                 msg : msg,
5120                 buttons: this.OKCANCEL,
5121                 fn: fn,
5122                 minWidth:250,
5123                 scope : scope,
5124                 prompt:true,
5125                 multiline: multiline,
5126                 modal : true
5127             });
5128             return this;
5129         },
5130
5131         /**
5132          * Button config that displays a single OK button
5133          * @type Object
5134          */
5135         OK : {ok:true},
5136         /**
5137          * Button config that displays Yes and No buttons
5138          * @type Object
5139          */
5140         YESNO : {yes:true, no:true},
5141         /**
5142          * Button config that displays OK and Cancel buttons
5143          * @type Object
5144          */
5145         OKCANCEL : {ok:true, cancel:true},
5146         /**
5147          * Button config that displays Yes, No and Cancel buttons
5148          * @type Object
5149          */
5150         YESNOCANCEL : {yes:true, no:true, cancel:true},
5151
5152         /**
5153          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5154          * @type Number
5155          */
5156         defaultTextHeight : 75,
5157         /**
5158          * The maximum width in pixels of the message box (defaults to 600)
5159          * @type Number
5160          */
5161         maxWidth : 600,
5162         /**
5163          * The minimum width in pixels of the message box (defaults to 100)
5164          * @type Number
5165          */
5166         minWidth : 100,
5167         /**
5168          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5169          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5170          * @type Number
5171          */
5172         minProgressWidth : 250,
5173         /**
5174          * An object containing the default button text strings that can be overriden for localized language support.
5175          * Supported properties are: ok, cancel, yes and no.
5176          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5177          * @type Object
5178          */
5179         buttonText : {
5180             ok : "OK",
5181             cancel : "Cancel",
5182             yes : "Yes",
5183             no : "No"
5184         }
5185     };
5186 }();
5187
5188 /**
5189  * Shorthand for {@link Roo.MessageBox}
5190  */
5191 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5192 Roo.Msg = Roo.Msg || Roo.MessageBox;
5193 /*
5194  * - LGPL
5195  *
5196  * navbar
5197  * 
5198  */
5199
5200 /**
5201  * @class Roo.bootstrap.Navbar
5202  * @extends Roo.bootstrap.Component
5203  * Bootstrap Navbar class
5204
5205  * @constructor
5206  * Create a new Navbar
5207  * @param {Object} config The config object
5208  */
5209
5210
5211 Roo.bootstrap.Navbar = function(config){
5212     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5213     this.addEvents({
5214         // raw events
5215         /**
5216          * @event beforetoggle
5217          * Fire before toggle the menu
5218          * @param {Roo.EventObject} e
5219          */
5220         "beforetoggle" : true
5221     });
5222 };
5223
5224 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5225     
5226     
5227    
5228     // private
5229     navItems : false,
5230     loadMask : false,
5231     
5232     
5233     getAutoCreate : function(){
5234         
5235         
5236         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5237         
5238     },
5239     
5240     initEvents :function ()
5241     {
5242         //Roo.log(this.el.select('.navbar-toggle',true));
5243         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5244         
5245         var mark = {
5246             tag: "div",
5247             cls:"x-dlg-mask"
5248         };
5249         
5250         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5251         
5252         var size = this.el.getSize();
5253         this.maskEl.setSize(size.width, size.height);
5254         this.maskEl.enableDisplayMode("block");
5255         this.maskEl.hide();
5256         
5257         if(this.loadMask){
5258             this.maskEl.show();
5259         }
5260     },
5261     
5262     
5263     getChildContainer : function()
5264     {
5265         if (this.el && this.el.select('.collapse').getCount()) {
5266             return this.el.select('.collapse',true).first();
5267         }
5268         
5269         return this.el;
5270     },
5271     
5272     mask : function()
5273     {
5274         this.maskEl.show();
5275     },
5276     
5277     unmask : function()
5278     {
5279         this.maskEl.hide();
5280     },
5281     onToggle : function()
5282     {
5283         
5284         if(this.fireEvent('beforetoggle', this) === false){
5285             return;
5286         }
5287         var ce = this.el.select('.navbar-collapse',true).first();
5288       
5289         if (!ce.hasClass('show')) {
5290            this.expand();
5291         } else {
5292             this.collapse();
5293         }
5294         
5295         
5296     
5297     },
5298     /**
5299      * Expand the navbar pulldown 
5300      */
5301     expand : function ()
5302     {
5303        
5304         var ce = this.el.select('.navbar-collapse',true).first();
5305         if (ce.hasClass('collapsing')) {
5306             return;
5307         }
5308         ce.dom.style.height = '';
5309                // show it...
5310         ce.addClass('in'); // old...
5311         ce.removeClass('collapse');
5312         ce.addClass('show');
5313         var h = ce.getHeight();
5314         Roo.log(h);
5315         ce.removeClass('show');
5316         // at this point we should be able to see it..
5317         ce.addClass('collapsing');
5318         
5319         ce.setHeight(0); // resize it ...
5320         ce.on('transitionend', function() {
5321             //Roo.log('done transition');
5322             ce.removeClass('collapsing');
5323             ce.addClass('show');
5324             ce.removeClass('collapse');
5325
5326             ce.dom.style.height = '';
5327         }, this, { single: true} );
5328         ce.setHeight(h);
5329         ce.dom.scrollTop = 0;
5330     },
5331     /**
5332      * Collapse the navbar pulldown 
5333      */
5334     collapse : function()
5335     {
5336          var ce = this.el.select('.navbar-collapse',true).first();
5337        
5338         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5339             // it's collapsed or collapsing..
5340             return;
5341         }
5342         ce.removeClass('in'); // old...
5343         ce.setHeight(ce.getHeight());
5344         ce.removeClass('show');
5345         ce.addClass('collapsing');
5346         
5347         ce.on('transitionend', function() {
5348             ce.dom.style.height = '';
5349             ce.removeClass('collapsing');
5350             ce.addClass('collapse');
5351         }, this, { single: true} );
5352         ce.setHeight(0);
5353     }
5354     
5355     
5356     
5357 });
5358
5359
5360
5361  
5362
5363  /*
5364  * - LGPL
5365  *
5366  * navbar
5367  * 
5368  */
5369
5370 /**
5371  * @class Roo.bootstrap.NavSimplebar
5372  * @extends Roo.bootstrap.Navbar
5373  * Bootstrap Sidebar class
5374  *
5375  * @cfg {Boolean} inverse is inverted color
5376  * 
5377  * @cfg {String} type (nav | pills | tabs)
5378  * @cfg {Boolean} arrangement stacked | justified
5379  * @cfg {String} align (left | right) alignment
5380  * 
5381  * @cfg {Boolean} main (true|false) main nav bar? default false
5382  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5383  * 
5384  * @cfg {String} tag (header|footer|nav|div) default is nav 
5385
5386  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5387  * 
5388  * 
5389  * @constructor
5390  * Create a new Sidebar
5391  * @param {Object} config The config object
5392  */
5393
5394
5395 Roo.bootstrap.NavSimplebar = function(config){
5396     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5397 };
5398
5399 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5400     
5401     inverse: false,
5402     
5403     type: false,
5404     arrangement: '',
5405     align : false,
5406     
5407     weight : 'light',
5408     
5409     main : false,
5410     
5411     
5412     tag : false,
5413     
5414     
5415     getAutoCreate : function(){
5416         
5417         
5418         var cfg = {
5419             tag : this.tag || 'div',
5420             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5421         };
5422         if (['light','white'].indexOf(this.weight) > -1) {
5423             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5424         }
5425         cfg.cls += ' bg-' + this.weight;
5426         
5427         if (this.inverse) {
5428             cfg.cls += ' navbar-inverse';
5429             
5430         }
5431         
5432         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5433         
5434         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5435             return cfg;
5436         }
5437         
5438         
5439     
5440         
5441         cfg.cn = [
5442             {
5443                 cls: 'nav nav-' + this.xtype,
5444                 tag : 'ul'
5445             }
5446         ];
5447         
5448          
5449         this.type = this.type || 'nav';
5450         if (['tabs','pills'].indexOf(this.type) != -1) {
5451             cfg.cn[0].cls += ' nav-' + this.type
5452         
5453         
5454         } else {
5455             if (this.type!=='nav') {
5456                 Roo.log('nav type must be nav/tabs/pills')
5457             }
5458             cfg.cn[0].cls += ' navbar-nav'
5459         }
5460         
5461         
5462         
5463         
5464         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5465             cfg.cn[0].cls += ' nav-' + this.arrangement;
5466         }
5467         
5468         
5469         if (this.align === 'right') {
5470             cfg.cn[0].cls += ' navbar-right';
5471         }
5472         
5473         
5474         
5475         
5476         return cfg;
5477     
5478         
5479     }
5480     
5481     
5482     
5483 });
5484
5485
5486
5487  
5488
5489  
5490        /*
5491  * - LGPL
5492  *
5493  * navbar
5494  * navbar-fixed-top
5495  * navbar-expand-md  fixed-top 
5496  */
5497
5498 /**
5499  * @class Roo.bootstrap.NavHeaderbar
5500  * @extends Roo.bootstrap.NavSimplebar
5501  * Bootstrap Sidebar class
5502  *
5503  * @cfg {String} brand what is brand
5504  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5505  * @cfg {String} brand_href href of the brand
5506  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5507  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5508  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5509  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5510  * 
5511  * @constructor
5512  * Create a new Sidebar
5513  * @param {Object} config The config object
5514  */
5515
5516
5517 Roo.bootstrap.NavHeaderbar = function(config){
5518     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5519       
5520 };
5521
5522 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5523     
5524     position: '',
5525     brand: '',
5526     brand_href: false,
5527     srButton : true,
5528     autohide : false,
5529     desktopCenter : false,
5530    
5531     
5532     getAutoCreate : function(){
5533         
5534         var   cfg = {
5535             tag: this.nav || 'nav',
5536             cls: 'navbar navbar-expand-md',
5537             role: 'navigation',
5538             cn: []
5539         };
5540         
5541         var cn = cfg.cn;
5542         if (this.desktopCenter) {
5543             cn.push({cls : 'container', cn : []});
5544             cn = cn[0].cn;
5545         }
5546         
5547         if(this.srButton){
5548             var btn = {
5549                 tag: 'button',
5550                 type: 'button',
5551                 cls: 'navbar-toggle navbar-toggler',
5552                 'data-toggle': 'collapse',
5553                 cn: [
5554                     {
5555                         tag: 'span',
5556                         cls: 'sr-only',
5557                         html: 'Toggle navigation'
5558                     },
5559                     {
5560                         tag: 'span',
5561                         cls: 'icon-bar navbar-toggler-icon'
5562                     },
5563                     {
5564                         tag: 'span',
5565                         cls: 'icon-bar'
5566                     },
5567                     {
5568                         tag: 'span',
5569                         cls: 'icon-bar'
5570                     }
5571                 ]
5572             };
5573             
5574             cn.push( Roo.bootstrap.version == 4 ? btn : {
5575                 tag: 'div',
5576                 cls: 'navbar-header',
5577                 cn: [
5578                     btn
5579                 ]
5580             });
5581         }
5582         
5583         cn.push({
5584             tag: 'div',
5585             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5586             cn : []
5587         });
5588         
5589         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5590         
5591         if (['light','white'].indexOf(this.weight) > -1) {
5592             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5593         }
5594         cfg.cls += ' bg-' + this.weight;
5595         
5596         
5597         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5598             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5599             
5600             // tag can override this..
5601             
5602             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5603         }
5604         
5605         if (this.brand !== '') {
5606             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5607             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5608                 tag: 'a',
5609                 href: this.brand_href ? this.brand_href : '#',
5610                 cls: 'navbar-brand',
5611                 cn: [
5612                 this.brand
5613                 ]
5614             });
5615         }
5616         
5617         if(this.main){
5618             cfg.cls += ' main-nav';
5619         }
5620         
5621         
5622         return cfg;
5623
5624         
5625     },
5626     getHeaderChildContainer : function()
5627     {
5628         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5629             return this.el.select('.navbar-header',true).first();
5630         }
5631         
5632         return this.getChildContainer();
5633     },
5634     
5635     getChildContainer : function()
5636     {
5637          
5638         return this.el.select('.roo-navbar-collapse',true).first();
5639          
5640         
5641     },
5642     
5643     initEvents : function()
5644     {
5645         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5646         
5647         if (this.autohide) {
5648             
5649             var prevScroll = 0;
5650             var ft = this.el;
5651             
5652             Roo.get(document).on('scroll',function(e) {
5653                 var ns = Roo.get(document).getScroll().top;
5654                 var os = prevScroll;
5655                 prevScroll = ns;
5656                 
5657                 if(ns > os){
5658                     ft.removeClass('slideDown');
5659                     ft.addClass('slideUp');
5660                     return;
5661                 }
5662                 ft.removeClass('slideUp');
5663                 ft.addClass('slideDown');
5664                  
5665               
5666           },this);
5667         }
5668     }    
5669     
5670 });
5671
5672
5673
5674  
5675
5676  /*
5677  * - LGPL
5678  *
5679  * navbar
5680  * 
5681  */
5682
5683 /**
5684  * @class Roo.bootstrap.NavSidebar
5685  * @extends Roo.bootstrap.Navbar
5686  * Bootstrap Sidebar class
5687  * 
5688  * @constructor
5689  * Create a new Sidebar
5690  * @param {Object} config The config object
5691  */
5692
5693
5694 Roo.bootstrap.NavSidebar = function(config){
5695     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5696 };
5697
5698 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5699     
5700     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5701     
5702     getAutoCreate : function(){
5703         
5704         
5705         return  {
5706             tag: 'div',
5707             cls: 'sidebar sidebar-nav'
5708         };
5709     
5710         
5711     }
5712     
5713     
5714     
5715 });
5716
5717
5718
5719  
5720
5721  /*
5722  * - LGPL
5723  *
5724  * nav group
5725  * 
5726  */
5727
5728 /**
5729  * @class Roo.bootstrap.NavGroup
5730  * @extends Roo.bootstrap.Component
5731  * Bootstrap NavGroup class
5732  * @cfg {String} align (left|right)
5733  * @cfg {Boolean} inverse
5734  * @cfg {String} type (nav|pills|tab) default nav
5735  * @cfg {String} navId - reference Id for navbar.
5736  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
5737  * 
5738  * @constructor
5739  * Create a new nav group
5740  * @param {Object} config The config object
5741  */
5742
5743 Roo.bootstrap.NavGroup = function(config){
5744     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5745     this.navItems = [];
5746    
5747     Roo.bootstrap.NavGroup.register(this);
5748      this.addEvents({
5749         /**
5750              * @event changed
5751              * Fires when the active item changes
5752              * @param {Roo.bootstrap.NavGroup} this
5753              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5754              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
5755          */
5756         'changed': true
5757      });
5758     
5759 };
5760
5761 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
5762     
5763     align: '',
5764     inverse: false,
5765     form: false,
5766     type: 'nav',
5767     navId : '',
5768     // private
5769     pilltype : true,
5770     
5771     navItems : false, 
5772     
5773     getAutoCreate : function()
5774     {
5775         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5776         
5777         cfg = {
5778             tag : 'ul',
5779             cls: 'nav' 
5780         };
5781         if (Roo.bootstrap.version == 4) {
5782             if (['tabs','pills'].indexOf(this.type) != -1) {
5783                 cfg.cls += ' nav-' + this.type; 
5784             } else {
5785                 // trying to remove so header bar can right align top?
5786                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5787                     // do not use on header bar... 
5788                     cfg.cls += ' navbar-nav';
5789                 }
5790             }
5791             
5792         } else {
5793             if (['tabs','pills'].indexOf(this.type) != -1) {
5794                 cfg.cls += ' nav-' + this.type
5795             } else {
5796                 if (this.type !== 'nav') {
5797                     Roo.log('nav type must be nav/tabs/pills')
5798                 }
5799                 cfg.cls += ' navbar-nav'
5800             }
5801         }
5802         
5803         if (this.parent() && this.parent().sidebar) {
5804             cfg = {
5805                 tag: 'ul',
5806                 cls: 'dashboard-menu sidebar-menu'
5807             };
5808             
5809             return cfg;
5810         }
5811         
5812         if (this.form === true) {
5813             cfg = {
5814                 tag: 'form',
5815                 cls: 'navbar-form form-inline'
5816             };
5817             //nav navbar-right ml-md-auto
5818             if (this.align === 'right') {
5819                 cfg.cls += ' navbar-right ml-md-auto';
5820             } else {
5821                 cfg.cls += ' navbar-left';
5822             }
5823         }
5824         
5825         if (this.align === 'right') {
5826             cfg.cls += ' navbar-right ml-md-auto';
5827         } else {
5828             cfg.cls += ' mr-auto';
5829         }
5830         
5831         if (this.inverse) {
5832             cfg.cls += ' navbar-inverse';
5833             
5834         }
5835         
5836         
5837         return cfg;
5838     },
5839     /**
5840     * sets the active Navigation item
5841     * @param {Roo.bootstrap.NavItem} the new current navitem
5842     */
5843     setActiveItem : function(item)
5844     {
5845         var prev = false;
5846         Roo.each(this.navItems, function(v){
5847             if (v == item) {
5848                 return ;
5849             }
5850             if (v.isActive()) {
5851                 v.setActive(false, true);
5852                 prev = v;
5853                 
5854             }
5855             
5856         });
5857
5858         item.setActive(true, true);
5859         this.fireEvent('changed', this, item, prev);
5860         
5861         
5862     },
5863     /**
5864     * gets the active Navigation item
5865     * @return {Roo.bootstrap.NavItem} the current navitem
5866     */
5867     getActive : function()
5868     {
5869         
5870         var prev = false;
5871         Roo.each(this.navItems, function(v){
5872             
5873             if (v.isActive()) {
5874                 prev = v;
5875                 
5876             }
5877             
5878         });
5879         return prev;
5880     },
5881     
5882     indexOfNav : function()
5883     {
5884         
5885         var prev = false;
5886         Roo.each(this.navItems, function(v,i){
5887             
5888             if (v.isActive()) {
5889                 prev = i;
5890                 
5891             }
5892             
5893         });
5894         return prev;
5895     },
5896     /**
5897     * adds a Navigation item
5898     * @param {Roo.bootstrap.NavItem} the navitem to add
5899     */
5900     addItem : function(cfg)
5901     {
5902         if (this.form && Roo.bootstrap.version == 4) {
5903             cfg.tag = 'div';
5904         }
5905         var cn = new Roo.bootstrap.NavItem(cfg);
5906         this.register(cn);
5907         cn.parentId = this.id;
5908         cn.onRender(this.el, null);
5909         return cn;
5910     },
5911     /**
5912     * register a Navigation item
5913     * @param {Roo.bootstrap.NavItem} the navitem to add
5914     */
5915     register : function(item)
5916     {
5917         this.navItems.push( item);
5918         item.navId = this.navId;
5919     
5920     },
5921     
5922     /**
5923     * clear all the Navigation item
5924     */
5925    
5926     clearAll : function()
5927     {
5928         this.navItems = [];
5929         this.el.dom.innerHTML = '';
5930     },
5931     
5932     getNavItem: function(tabId)
5933     {
5934         var ret = false;
5935         Roo.each(this.navItems, function(e) {
5936             if (e.tabId == tabId) {
5937                ret =  e;
5938                return false;
5939             }
5940             return true;
5941             
5942         });
5943         return ret;
5944     },
5945     
5946     setActiveNext : function()
5947     {
5948         var i = this.indexOfNav(this.getActive());
5949         if (i > this.navItems.length) {
5950             return;
5951         }
5952         this.setActiveItem(this.navItems[i+1]);
5953     },
5954     setActivePrev : function()
5955     {
5956         var i = this.indexOfNav(this.getActive());
5957         if (i  < 1) {
5958             return;
5959         }
5960         this.setActiveItem(this.navItems[i-1]);
5961     },
5962     clearWasActive : function(except) {
5963         Roo.each(this.navItems, function(e) {
5964             if (e.tabId != except.tabId && e.was_active) {
5965                e.was_active = false;
5966                return false;
5967             }
5968             return true;
5969             
5970         });
5971     },
5972     getWasActive : function ()
5973     {
5974         var r = false;
5975         Roo.each(this.navItems, function(e) {
5976             if (e.was_active) {
5977                r = e;
5978                return false;
5979             }
5980             return true;
5981             
5982         });
5983         return r;
5984     }
5985     
5986     
5987 });
5988
5989  
5990 Roo.apply(Roo.bootstrap.NavGroup, {
5991     
5992     groups: {},
5993      /**
5994     * register a Navigation Group
5995     * @param {Roo.bootstrap.NavGroup} the navgroup to add
5996     */
5997     register : function(navgrp)
5998     {
5999         this.groups[navgrp.navId] = navgrp;
6000         
6001     },
6002     /**
6003     * fetch a Navigation Group based on the navigation ID
6004     * @param {string} the navgroup to add
6005     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6006     */
6007     get: function(navId) {
6008         if (typeof(this.groups[navId]) == 'undefined') {
6009             return false;
6010             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6011         }
6012         return this.groups[navId] ;
6013     }
6014     
6015     
6016     
6017 });
6018
6019  /*
6020  * - LGPL
6021  *
6022  * row
6023  * 
6024  */
6025
6026 /**
6027  * @class Roo.bootstrap.NavItem
6028  * @extends Roo.bootstrap.Component
6029  * Bootstrap Navbar.NavItem class
6030  * @cfg {String} href  link to
6031  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6032  * @cfg {Boolean} button_outline show and outlined button
6033  * @cfg {String} html content of button
6034  * @cfg {String} badge text inside badge
6035  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6036  * @cfg {String} glyphicon DEPRICATED - use fa
6037  * @cfg {String} icon DEPRICATED - use fa
6038  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6039  * @cfg {Boolean} active Is item active
6040  * @cfg {Boolean} disabled Is item disabled
6041  * @cfg {String} linkcls  Link Class
6042  * @cfg {Boolean} preventDefault (true | false) default false
6043  * @cfg {String} tabId the tab that this item activates.
6044  * @cfg {String} tagtype (a|span) render as a href or span?
6045  * @cfg {Boolean} animateRef (true|false) link to element default false  
6046   
6047  * @constructor
6048  * Create a new Navbar Item
6049  * @param {Object} config The config object
6050  */
6051 Roo.bootstrap.NavItem = function(config){
6052     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6053     this.addEvents({
6054         // raw events
6055         /**
6056          * @event click
6057          * The raw click event for the entire grid.
6058          * @param {Roo.EventObject} e
6059          */
6060         "click" : true,
6061          /**
6062             * @event changed
6063             * Fires when the active item active state changes
6064             * @param {Roo.bootstrap.NavItem} this
6065             * @param {boolean} state the new state
6066              
6067          */
6068         'changed': true,
6069         /**
6070             * @event scrollto
6071             * Fires when scroll to element
6072             * @param {Roo.bootstrap.NavItem} this
6073             * @param {Object} options
6074             * @param {Roo.EventObject} e
6075              
6076          */
6077         'scrollto': true
6078     });
6079    
6080 };
6081
6082 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6083     
6084     href: false,
6085     html: '',
6086     badge: '',
6087     icon: false,
6088     fa : false,
6089     glyphicon: false,
6090     active: false,
6091     preventDefault : false,
6092     tabId : false,
6093     tagtype : 'a',
6094     tag: 'li',
6095     disabled : false,
6096     animateRef : false,
6097     was_active : false,
6098     button_weight : '',
6099     button_outline : false,
6100     linkcls : '',
6101     navLink: false,
6102     
6103     getAutoCreate : function(){
6104          
6105         var cfg = {
6106             tag: this.tag,
6107             cls: 'nav-item'
6108         };
6109         
6110         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6111         
6112         if (this.active) {
6113             cfg.cls +=  ' active' ;
6114         }
6115         if (this.disabled) {
6116             cfg.cls += ' disabled';
6117         }
6118         
6119         // BS4 only?
6120         if (this.button_weight.length) {
6121             cfg.tag = this.href ? 'a' : 'button';
6122             cfg.html = this.html || '';
6123             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6124             if (this.href) {
6125                 cfg.href = this.href;
6126             }
6127             if (this.fa) {
6128                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6129             }
6130             
6131             // menu .. should add dropdown-menu class - so no need for carat..
6132             
6133             if (this.badge !== '') {
6134                  
6135                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6136             }
6137             return cfg;
6138         }
6139         
6140         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6141             cfg.cn = [
6142                 {
6143                     tag: this.tagtype,
6144                     href : this.href || "#",
6145                     html: this.html || ''
6146                 }
6147             ];
6148             if (this.tagtype == 'a') {
6149                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6150         
6151             }
6152             if (this.icon) {
6153                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6154             }
6155             if (this.fa) {
6156                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6157             }
6158             if(this.glyphicon) {
6159                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6160             }
6161             
6162             if (this.menu) {
6163                 
6164                 cfg.cn[0].html += " <span class='caret'></span>";
6165              
6166             }
6167             
6168             if (this.badge !== '') {
6169                  
6170                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6171             }
6172         }
6173         
6174         
6175         
6176         return cfg;
6177     },
6178     onRender : function(ct, position)
6179     {
6180        // Roo.log("Call onRender: " + this.xtype);
6181         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6182             this.tag = 'div';
6183         }
6184         
6185         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6186         this.navLink = this.el.select('.nav-link',true).first();
6187         return ret;
6188     },
6189       
6190     
6191     initEvents: function() 
6192     {
6193         if (typeof (this.menu) != 'undefined') {
6194             this.menu.parentType = this.xtype;
6195             this.menu.triggerEl = this.el;
6196             this.menu = this.addxtype(Roo.apply({}, this.menu));
6197         }
6198         
6199         this.el.on('click', this.onClick, this);
6200         
6201         //if(this.tagtype == 'span'){
6202         //    this.el.select('span',true).on('click', this.onClick, this);
6203         //}
6204        
6205         // at this point parent should be available..
6206         this.parent().register(this);
6207     },
6208     
6209     onClick : function(e)
6210     {
6211         if (e.getTarget('.dropdown-menu-item')) {
6212             // did you click on a menu itemm.... - then don't trigger onclick..
6213             return;
6214         }
6215         
6216         if(
6217                 this.preventDefault || 
6218                 this.href == '#' 
6219         ){
6220             Roo.log("NavItem - prevent Default?");
6221             e.preventDefault();
6222         }
6223         
6224         if (this.disabled) {
6225             return;
6226         }
6227         
6228         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6229         if (tg && tg.transition) {
6230             Roo.log("waiting for the transitionend");
6231             return;
6232         }
6233         
6234         
6235         
6236         //Roo.log("fire event clicked");
6237         if(this.fireEvent('click', this, e) === false){
6238             return;
6239         };
6240         
6241         if(this.tagtype == 'span'){
6242             return;
6243         }
6244         
6245         //Roo.log(this.href);
6246         var ael = this.el.select('a',true).first();
6247         //Roo.log(ael);
6248         
6249         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6250             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6251             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6252                 return; // ignore... - it's a 'hash' to another page.
6253             }
6254             Roo.log("NavItem - prevent Default?");
6255             e.preventDefault();
6256             this.scrollToElement(e);
6257         }
6258         
6259         
6260         var p =  this.parent();
6261    
6262         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6263             if (typeof(p.setActiveItem) !== 'undefined') {
6264                 p.setActiveItem(this);
6265             }
6266         }
6267         
6268         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6269         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6270             // remove the collapsed menu expand...
6271             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6272         }
6273     },
6274     
6275     isActive: function () {
6276         return this.active
6277     },
6278     setActive : function(state, fire, is_was_active)
6279     {
6280         if (this.active && !state && this.navId) {
6281             this.was_active = true;
6282             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6283             if (nv) {
6284                 nv.clearWasActive(this);
6285             }
6286             
6287         }
6288         this.active = state;
6289         
6290         if (!state ) {
6291             this.el.removeClass('active');
6292             this.navLink ? this.navLink.removeClass('active') : false;
6293         } else if (!this.el.hasClass('active')) {
6294             
6295             this.el.addClass('active');
6296             if (Roo.bootstrap.version == 4 && this.navLink ) {
6297                 this.navLink.addClass('active');
6298             }
6299             
6300         }
6301         if (fire) {
6302             this.fireEvent('changed', this, state);
6303         }
6304         
6305         // show a panel if it's registered and related..
6306         
6307         if (!this.navId || !this.tabId || !state || is_was_active) {
6308             return;
6309         }
6310         
6311         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6312         if (!tg) {
6313             return;
6314         }
6315         var pan = tg.getPanelByName(this.tabId);
6316         if (!pan) {
6317             return;
6318         }
6319         // if we can not flip to new panel - go back to old nav highlight..
6320         if (false == tg.showPanel(pan)) {
6321             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6322             if (nv) {
6323                 var onav = nv.getWasActive();
6324                 if (onav) {
6325                     onav.setActive(true, false, true);
6326                 }
6327             }
6328             
6329         }
6330         
6331         
6332         
6333     },
6334      // this should not be here...
6335     setDisabled : function(state)
6336     {
6337         this.disabled = state;
6338         if (!state ) {
6339             this.el.removeClass('disabled');
6340         } else if (!this.el.hasClass('disabled')) {
6341             this.el.addClass('disabled');
6342         }
6343         
6344     },
6345     
6346     /**
6347      * Fetch the element to display the tooltip on.
6348      * @return {Roo.Element} defaults to this.el
6349      */
6350     tooltipEl : function()
6351     {
6352         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6353     },
6354     
6355     scrollToElement : function(e)
6356     {
6357         var c = document.body;
6358         
6359         /*
6360          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6361          */
6362         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6363             c = document.documentElement;
6364         }
6365         
6366         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6367         
6368         if(!target){
6369             return;
6370         }
6371
6372         var o = target.calcOffsetsTo(c);
6373         
6374         var options = {
6375             target : target,
6376             value : o[1]
6377         };
6378         
6379         this.fireEvent('scrollto', this, options, e);
6380         
6381         Roo.get(c).scrollTo('top', options.value, true);
6382         
6383         return;
6384     }
6385 });
6386  
6387
6388  /*
6389  * - LGPL
6390  *
6391  * sidebar item
6392  *
6393  *  li
6394  *    <span> icon </span>
6395  *    <span> text </span>
6396  *    <span>badge </span>
6397  */
6398
6399 /**
6400  * @class Roo.bootstrap.NavSidebarItem
6401  * @extends Roo.bootstrap.NavItem
6402  * Bootstrap Navbar.NavSidebarItem class
6403  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6404  * {Boolean} open is the menu open
6405  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6406  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6407  * {String} buttonSize (sm|md|lg)the extra classes for the button
6408  * {Boolean} showArrow show arrow next to the text (default true)
6409  * @constructor
6410  * Create a new Navbar Button
6411  * @param {Object} config The config object
6412  */
6413 Roo.bootstrap.NavSidebarItem = function(config){
6414     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6415     this.addEvents({
6416         // raw events
6417         /**
6418          * @event click
6419          * The raw click event for the entire grid.
6420          * @param {Roo.EventObject} e
6421          */
6422         "click" : true,
6423          /**
6424             * @event changed
6425             * Fires when the active item active state changes
6426             * @param {Roo.bootstrap.NavSidebarItem} this
6427             * @param {boolean} state the new state
6428              
6429          */
6430         'changed': true
6431     });
6432    
6433 };
6434
6435 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6436     
6437     badgeWeight : 'default',
6438     
6439     open: false,
6440     
6441     buttonView : false,
6442     
6443     buttonWeight : 'default',
6444     
6445     buttonSize : 'md',
6446     
6447     showArrow : true,
6448     
6449     getAutoCreate : function(){
6450         
6451         
6452         var a = {
6453                 tag: 'a',
6454                 href : this.href || '#',
6455                 cls: '',
6456                 html : '',
6457                 cn : []
6458         };
6459         
6460         if(this.buttonView){
6461             a = {
6462                 tag: 'button',
6463                 href : this.href || '#',
6464                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6465                 html : this.html,
6466                 cn : []
6467             };
6468         }
6469         
6470         var cfg = {
6471             tag: 'li',
6472             cls: '',
6473             cn: [ a ]
6474         };
6475         
6476         if (this.active) {
6477             cfg.cls += ' active';
6478         }
6479         
6480         if (this.disabled) {
6481             cfg.cls += ' disabled';
6482         }
6483         if (this.open) {
6484             cfg.cls += ' open x-open';
6485         }
6486         // left icon..
6487         if (this.glyphicon || this.icon) {
6488             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6489             a.cn.push({ tag : 'i', cls : c }) ;
6490         }
6491         
6492         if(!this.buttonView){
6493             var span = {
6494                 tag: 'span',
6495                 html : this.html || ''
6496             };
6497
6498             a.cn.push(span);
6499             
6500         }
6501         
6502         if (this.badge !== '') {
6503             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6504         }
6505         
6506         if (this.menu) {
6507             
6508             if(this.showArrow){
6509                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6510             }
6511             
6512             a.cls += ' dropdown-toggle treeview' ;
6513         }
6514         
6515         return cfg;
6516     },
6517     
6518     initEvents : function()
6519     { 
6520         if (typeof (this.menu) != 'undefined') {
6521             this.menu.parentType = this.xtype;
6522             this.menu.triggerEl = this.el;
6523             this.menu = this.addxtype(Roo.apply({}, this.menu));
6524         }
6525         
6526         this.el.on('click', this.onClick, this);
6527         
6528         if(this.badge !== ''){
6529             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6530         }
6531         
6532     },
6533     
6534     onClick : function(e)
6535     {
6536         if(this.disabled){
6537             e.preventDefault();
6538             return;
6539         }
6540         
6541         if(this.preventDefault){
6542             e.preventDefault();
6543         }
6544         
6545         this.fireEvent('click', this, e);
6546     },
6547     
6548     disable : function()
6549     {
6550         this.setDisabled(true);
6551     },
6552     
6553     enable : function()
6554     {
6555         this.setDisabled(false);
6556     },
6557     
6558     setDisabled : function(state)
6559     {
6560         if(this.disabled == state){
6561             return;
6562         }
6563         
6564         this.disabled = state;
6565         
6566         if (state) {
6567             this.el.addClass('disabled');
6568             return;
6569         }
6570         
6571         this.el.removeClass('disabled');
6572         
6573         return;
6574     },
6575     
6576     setActive : function(state)
6577     {
6578         if(this.active == state){
6579             return;
6580         }
6581         
6582         this.active = state;
6583         
6584         if (state) {
6585             this.el.addClass('active');
6586             return;
6587         }
6588         
6589         this.el.removeClass('active');
6590         
6591         return;
6592     },
6593     
6594     isActive: function () 
6595     {
6596         return this.active;
6597     },
6598     
6599     setBadge : function(str)
6600     {
6601         if(!this.badgeEl){
6602             return;
6603         }
6604         
6605         this.badgeEl.dom.innerHTML = str;
6606     }
6607     
6608    
6609      
6610  
6611 });
6612  
6613
6614  /*
6615  * - LGPL
6616  *
6617  *  Breadcrumb Nav
6618  * 
6619  */
6620 Roo.namespace('Roo.bootstrap.breadcrumb');
6621
6622
6623 /**
6624  * @class Roo.bootstrap.breadcrumb.Nav
6625  * @extends Roo.bootstrap.Component
6626  * Bootstrap Breadcrumb Nav Class
6627  *  
6628  * @children Roo.bootstrap.breadcrumb.Item
6629  * 
6630  * @constructor
6631  * Create a new breadcrumb.Nav
6632  * @param {Object} config The config object
6633  */
6634
6635
6636 Roo.bootstrap.breadcrumb.Nav = function(config){
6637     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6638     
6639     
6640 };
6641
6642 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6643     
6644     getAutoCreate : function()
6645     {
6646
6647         var cfg = {
6648             tag: 'nav',
6649             cn : [
6650                 {
6651                     tag : 'ol',
6652                     cls : 'breadcrumb'
6653                 }
6654             ]
6655             
6656         };
6657           
6658         return cfg;
6659     },
6660     
6661     initEvents: function()
6662     {
6663         this.olEl = this.el.select('ol',true).first();    
6664     },
6665     getChildContainer : function()
6666     {
6667         return this.olEl;  
6668     }
6669     
6670 });
6671
6672  /*
6673  * - LGPL
6674  *
6675  *  Breadcrumb Item
6676  * 
6677  */
6678
6679
6680 /**
6681  * @class Roo.bootstrap.breadcrumb.Nav
6682  * @extends Roo.bootstrap.Component
6683  * Bootstrap Breadcrumb Nav Class
6684  *  
6685  * @children Roo.bootstrap.breadcrumb.Component
6686  * @cfg {String} html the content of the link.
6687  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6688  * @cfg {Boolean} active is it active
6689
6690  * 
6691  * @constructor
6692  * Create a new breadcrumb.Nav
6693  * @param {Object} config The config object
6694  */
6695
6696 Roo.bootstrap.breadcrumb.Item = function(config){
6697     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6698     this.addEvents({
6699         // img events
6700         /**
6701          * @event click
6702          * The img click event for the img.
6703          * @param {Roo.EventObject} e
6704          */
6705         "click" : true
6706     });
6707     
6708 };
6709
6710 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
6711     
6712     href: false,
6713     html : '',
6714     
6715     getAutoCreate : function()
6716     {
6717
6718         var cfg = {
6719             tag: 'li',
6720             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6721         };
6722         if (this.href !== false) {
6723             cfg.cn = [{
6724                 tag : 'a',
6725                 href : this.href,
6726                 html : this.html
6727             }];
6728         } else {
6729             cfg.html = this.html;
6730         }
6731         
6732         return cfg;
6733     },
6734     
6735     initEvents: function()
6736     {
6737         if (this.href) {
6738             this.el.select('a', true).first().on('click',this.onClick, this)
6739         }
6740         
6741     },
6742     onClick : function(e)
6743     {
6744         e.preventDefault();
6745         this.fireEvent('click',this,  e);
6746     }
6747     
6748 });
6749
6750  /*
6751  * - LGPL
6752  *
6753  * row
6754  * 
6755  */
6756
6757 /**
6758  * @class Roo.bootstrap.Row
6759  * @extends Roo.bootstrap.Component
6760  * Bootstrap Row class (contains columns...)
6761  * 
6762  * @constructor
6763  * Create a new Row
6764  * @param {Object} config The config object
6765  */
6766
6767 Roo.bootstrap.Row = function(config){
6768     Roo.bootstrap.Row.superclass.constructor.call(this, config);
6769 };
6770
6771 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
6772     
6773     getAutoCreate : function(){
6774        return {
6775             cls: 'row clearfix'
6776        };
6777     }
6778     
6779     
6780 });
6781
6782  
6783
6784  /*
6785  * - LGPL
6786  *
6787  * pagination
6788  * 
6789  */
6790
6791 /**
6792  * @class Roo.bootstrap.Pagination
6793  * @extends Roo.bootstrap.Component
6794  * Bootstrap Pagination class
6795  * @cfg {String} size xs | sm | md | lg
6796  * @cfg {Boolean} inverse false | true
6797  * 
6798  * @constructor
6799  * Create a new Pagination
6800  * @param {Object} config The config object
6801  */
6802
6803 Roo.bootstrap.Pagination = function(config){
6804     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6805 };
6806
6807 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
6808     
6809     cls: false,
6810     size: false,
6811     inverse: false,
6812     
6813     getAutoCreate : function(){
6814         var cfg = {
6815             tag: 'ul',
6816                 cls: 'pagination'
6817         };
6818         if (this.inverse) {
6819             cfg.cls += ' inverse';
6820         }
6821         if (this.html) {
6822             cfg.html=this.html;
6823         }
6824         if (this.cls) {
6825             cfg.cls += " " + this.cls;
6826         }
6827         return cfg;
6828     }
6829    
6830 });
6831
6832  
6833
6834  /*
6835  * - LGPL
6836  *
6837  * Pagination item
6838  * 
6839  */
6840
6841
6842 /**
6843  * @class Roo.bootstrap.PaginationItem
6844  * @extends Roo.bootstrap.Component
6845  * Bootstrap PaginationItem class
6846  * @cfg {String} html text
6847  * @cfg {String} href the link
6848  * @cfg {Boolean} preventDefault (true | false) default true
6849  * @cfg {Boolean} active (true | false) default false
6850  * @cfg {Boolean} disabled default false
6851  * 
6852  * 
6853  * @constructor
6854  * Create a new PaginationItem
6855  * @param {Object} config The config object
6856  */
6857
6858
6859 Roo.bootstrap.PaginationItem = function(config){
6860     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6861     this.addEvents({
6862         // raw events
6863         /**
6864          * @event click
6865          * The raw click event for the entire grid.
6866          * @param {Roo.EventObject} e
6867          */
6868         "click" : true
6869     });
6870 };
6871
6872 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
6873     
6874     href : false,
6875     html : false,
6876     preventDefault: true,
6877     active : false,
6878     cls : false,
6879     disabled: false,
6880     
6881     getAutoCreate : function(){
6882         var cfg= {
6883             tag: 'li',
6884             cn: [
6885                 {
6886                     tag : 'a',
6887                     href : this.href ? this.href : '#',
6888                     html : this.html ? this.html : ''
6889                 }
6890             ]
6891         };
6892         
6893         if(this.cls){
6894             cfg.cls = this.cls;
6895         }
6896         
6897         if(this.disabled){
6898             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6899         }
6900         
6901         if(this.active){
6902             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6903         }
6904         
6905         return cfg;
6906     },
6907     
6908     initEvents: function() {
6909         
6910         this.el.on('click', this.onClick, this);
6911         
6912     },
6913     onClick : function(e)
6914     {
6915         Roo.log('PaginationItem on click ');
6916         if(this.preventDefault){
6917             e.preventDefault();
6918         }
6919         
6920         if(this.disabled){
6921             return;
6922         }
6923         
6924         this.fireEvent('click', this, e);
6925     }
6926    
6927 });
6928
6929  
6930
6931  /*
6932  * - LGPL
6933  *
6934  * slider
6935  * 
6936  */
6937
6938
6939 /**
6940  * @class Roo.bootstrap.Slider
6941  * @extends Roo.bootstrap.Component
6942  * Bootstrap Slider class
6943  *    
6944  * @constructor
6945  * Create a new Slider
6946  * @param {Object} config The config object
6947  */
6948
6949 Roo.bootstrap.Slider = function(config){
6950     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6951 };
6952
6953 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
6954     
6955     getAutoCreate : function(){
6956         
6957         var cfg = {
6958             tag: 'div',
6959             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6960             cn: [
6961                 {
6962                     tag: 'a',
6963                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
6964                 }
6965             ]
6966         };
6967         
6968         return cfg;
6969     }
6970    
6971 });
6972
6973  /*
6974  * Based on:
6975  * Ext JS Library 1.1.1
6976  * Copyright(c) 2006-2007, Ext JS, LLC.
6977  *
6978  * Originally Released Under LGPL - original licence link has changed is not relivant.
6979  *
6980  * Fork - LGPL
6981  * <script type="text/javascript">
6982  */
6983  
6984
6985 /**
6986  * @class Roo.grid.ColumnModel
6987  * @extends Roo.util.Observable
6988  * This is the default implementation of a ColumnModel used by the Grid. It defines
6989  * the columns in the grid.
6990  * <br>Usage:<br>
6991  <pre><code>
6992  var colModel = new Roo.grid.ColumnModel([
6993         {header: "Ticker", width: 60, sortable: true, locked: true},
6994         {header: "Company Name", width: 150, sortable: true},
6995         {header: "Market Cap.", width: 100, sortable: true},
6996         {header: "$ Sales", width: 100, sortable: true, renderer: money},
6997         {header: "Employees", width: 100, sortable: true, resizable: false}
6998  ]);
6999  </code></pre>
7000  * <p>
7001  
7002  * The config options listed for this class are options which may appear in each
7003  * individual column definition.
7004  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7005  * @constructor
7006  * @param {Object} config An Array of column config objects. See this class's
7007  * config objects for details.
7008 */
7009 Roo.grid.ColumnModel = function(config){
7010         /**
7011      * The config passed into the constructor
7012      */
7013     this.config = config;
7014     this.lookup = {};
7015
7016     // if no id, create one
7017     // if the column does not have a dataIndex mapping,
7018     // map it to the order it is in the config
7019     for(var i = 0, len = config.length; i < len; i++){
7020         var c = config[i];
7021         if(typeof c.dataIndex == "undefined"){
7022             c.dataIndex = i;
7023         }
7024         if(typeof c.renderer == "string"){
7025             c.renderer = Roo.util.Format[c.renderer];
7026         }
7027         if(typeof c.id == "undefined"){
7028             c.id = Roo.id();
7029         }
7030         if(c.editor && c.editor.xtype){
7031             c.editor  = Roo.factory(c.editor, Roo.grid);
7032         }
7033         if(c.editor && c.editor.isFormField){
7034             c.editor = new Roo.grid.GridEditor(c.editor);
7035         }
7036         this.lookup[c.id] = c;
7037     }
7038
7039     /**
7040      * The width of columns which have no width specified (defaults to 100)
7041      * @type Number
7042      */
7043     this.defaultWidth = 100;
7044
7045     /**
7046      * Default sortable of columns which have no sortable specified (defaults to false)
7047      * @type Boolean
7048      */
7049     this.defaultSortable = false;
7050
7051     this.addEvents({
7052         /**
7053              * @event widthchange
7054              * Fires when the width of a column changes.
7055              * @param {ColumnModel} this
7056              * @param {Number} columnIndex The column index
7057              * @param {Number} newWidth The new width
7058              */
7059             "widthchange": true,
7060         /**
7061              * @event headerchange
7062              * Fires when the text of a header changes.
7063              * @param {ColumnModel} this
7064              * @param {Number} columnIndex The column index
7065              * @param {Number} newText The new header text
7066              */
7067             "headerchange": true,
7068         /**
7069              * @event hiddenchange
7070              * Fires when a column is hidden or "unhidden".
7071              * @param {ColumnModel} this
7072              * @param {Number} columnIndex The column index
7073              * @param {Boolean} hidden true if hidden, false otherwise
7074              */
7075             "hiddenchange": true,
7076             /**
7077          * @event columnmoved
7078          * Fires when a column is moved.
7079          * @param {ColumnModel} this
7080          * @param {Number} oldIndex
7081          * @param {Number} newIndex
7082          */
7083         "columnmoved" : true,
7084         /**
7085          * @event columlockchange
7086          * Fires when a column's locked state is changed
7087          * @param {ColumnModel} this
7088          * @param {Number} colIndex
7089          * @param {Boolean} locked true if locked
7090          */
7091         "columnlockchange" : true
7092     });
7093     Roo.grid.ColumnModel.superclass.constructor.call(this);
7094 };
7095 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7096     /**
7097      * @cfg {String} header The header text to display in the Grid view.
7098      */
7099     /**
7100      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7101      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7102      * specified, the column's index is used as an index into the Record's data Array.
7103      */
7104     /**
7105      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7106      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7107      */
7108     /**
7109      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7110      * Defaults to the value of the {@link #defaultSortable} property.
7111      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7112      */
7113     /**
7114      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7115      */
7116     /**
7117      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7118      */
7119     /**
7120      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7121      */
7122     /**
7123      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7124      */
7125     /**
7126      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7127      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7128      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7129      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7130      */
7131        /**
7132      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7133      */
7134     /**
7135      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7136      */
7137     /**
7138      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7139      */
7140     /**
7141      * @cfg {String} cursor (Optional)
7142      */
7143     /**
7144      * @cfg {String} tooltip (Optional)
7145      */
7146     /**
7147      * @cfg {Number} xs (Optional)
7148      */
7149     /**
7150      * @cfg {Number} sm (Optional)
7151      */
7152     /**
7153      * @cfg {Number} md (Optional)
7154      */
7155     /**
7156      * @cfg {Number} lg (Optional)
7157      */
7158     /**
7159      * Returns the id of the column at the specified index.
7160      * @param {Number} index The column index
7161      * @return {String} the id
7162      */
7163     getColumnId : function(index){
7164         return this.config[index].id;
7165     },
7166
7167     /**
7168      * Returns the column for a specified id.
7169      * @param {String} id The column id
7170      * @return {Object} the column
7171      */
7172     getColumnById : function(id){
7173         return this.lookup[id];
7174     },
7175
7176     
7177     /**
7178      * Returns the column for a specified dataIndex.
7179      * @param {String} dataIndex The column dataIndex
7180      * @return {Object|Boolean} the column or false if not found
7181      */
7182     getColumnByDataIndex: function(dataIndex){
7183         var index = this.findColumnIndex(dataIndex);
7184         return index > -1 ? this.config[index] : false;
7185     },
7186     
7187     /**
7188      * Returns the index for a specified column id.
7189      * @param {String} id The column id
7190      * @return {Number} the index, or -1 if not found
7191      */
7192     getIndexById : function(id){
7193         for(var i = 0, len = this.config.length; i < len; i++){
7194             if(this.config[i].id == id){
7195                 return i;
7196             }
7197         }
7198         return -1;
7199     },
7200     
7201     /**
7202      * Returns the index for a specified column dataIndex.
7203      * @param {String} dataIndex The column dataIndex
7204      * @return {Number} the index, or -1 if not found
7205      */
7206     
7207     findColumnIndex : function(dataIndex){
7208         for(var i = 0, len = this.config.length; i < len; i++){
7209             if(this.config[i].dataIndex == dataIndex){
7210                 return i;
7211             }
7212         }
7213         return -1;
7214     },
7215     
7216     
7217     moveColumn : function(oldIndex, newIndex){
7218         var c = this.config[oldIndex];
7219         this.config.splice(oldIndex, 1);
7220         this.config.splice(newIndex, 0, c);
7221         this.dataMap = null;
7222         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7223     },
7224
7225     isLocked : function(colIndex){
7226         return this.config[colIndex].locked === true;
7227     },
7228
7229     setLocked : function(colIndex, value, suppressEvent){
7230         if(this.isLocked(colIndex) == value){
7231             return;
7232         }
7233         this.config[colIndex].locked = value;
7234         if(!suppressEvent){
7235             this.fireEvent("columnlockchange", this, colIndex, value);
7236         }
7237     },
7238
7239     getTotalLockedWidth : function(){
7240         var totalWidth = 0;
7241         for(var i = 0; i < this.config.length; i++){
7242             if(this.isLocked(i) && !this.isHidden(i)){
7243                 this.totalWidth += this.getColumnWidth(i);
7244             }
7245         }
7246         return totalWidth;
7247     },
7248
7249     getLockedCount : function(){
7250         for(var i = 0, len = this.config.length; i < len; i++){
7251             if(!this.isLocked(i)){
7252                 return i;
7253             }
7254         }
7255         
7256         return this.config.length;
7257     },
7258
7259     /**
7260      * Returns the number of columns.
7261      * @return {Number}
7262      */
7263     getColumnCount : function(visibleOnly){
7264         if(visibleOnly === true){
7265             var c = 0;
7266             for(var i = 0, len = this.config.length; i < len; i++){
7267                 if(!this.isHidden(i)){
7268                     c++;
7269                 }
7270             }
7271             return c;
7272         }
7273         return this.config.length;
7274     },
7275
7276     /**
7277      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7278      * @param {Function} fn
7279      * @param {Object} scope (optional)
7280      * @return {Array} result
7281      */
7282     getColumnsBy : function(fn, scope){
7283         var r = [];
7284         for(var i = 0, len = this.config.length; i < len; i++){
7285             var c = this.config[i];
7286             if(fn.call(scope||this, c, i) === true){
7287                 r[r.length] = c;
7288             }
7289         }
7290         return r;
7291     },
7292
7293     /**
7294      * Returns true if the specified column is sortable.
7295      * @param {Number} col The column index
7296      * @return {Boolean}
7297      */
7298     isSortable : function(col){
7299         if(typeof this.config[col].sortable == "undefined"){
7300             return this.defaultSortable;
7301         }
7302         return this.config[col].sortable;
7303     },
7304
7305     /**
7306      * Returns the rendering (formatting) function defined for the column.
7307      * @param {Number} col The column index.
7308      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7309      */
7310     getRenderer : function(col){
7311         if(!this.config[col].renderer){
7312             return Roo.grid.ColumnModel.defaultRenderer;
7313         }
7314         return this.config[col].renderer;
7315     },
7316
7317     /**
7318      * Sets the rendering (formatting) function for a column.
7319      * @param {Number} col The column index
7320      * @param {Function} fn The function to use to process the cell's raw data
7321      * to return HTML markup for the grid view. The render function is called with
7322      * the following parameters:<ul>
7323      * <li>Data value.</li>
7324      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7325      * <li>css A CSS style string to apply to the table cell.</li>
7326      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7327      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7328      * <li>Row index</li>
7329      * <li>Column index</li>
7330      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7331      */
7332     setRenderer : function(col, fn){
7333         this.config[col].renderer = fn;
7334     },
7335
7336     /**
7337      * Returns the width for the specified column.
7338      * @param {Number} col The column index
7339      * @return {Number}
7340      */
7341     getColumnWidth : function(col){
7342         return this.config[col].width * 1 || this.defaultWidth;
7343     },
7344
7345     /**
7346      * Sets the width for a column.
7347      * @param {Number} col The column index
7348      * @param {Number} width The new width
7349      */
7350     setColumnWidth : function(col, width, suppressEvent){
7351         this.config[col].width = width;
7352         this.totalWidth = null;
7353         if(!suppressEvent){
7354              this.fireEvent("widthchange", this, col, width);
7355         }
7356     },
7357
7358     /**
7359      * Returns the total width of all columns.
7360      * @param {Boolean} includeHidden True to include hidden column widths
7361      * @return {Number}
7362      */
7363     getTotalWidth : function(includeHidden){
7364         if(!this.totalWidth){
7365             this.totalWidth = 0;
7366             for(var i = 0, len = this.config.length; i < len; i++){
7367                 if(includeHidden || !this.isHidden(i)){
7368                     this.totalWidth += this.getColumnWidth(i);
7369                 }
7370             }
7371         }
7372         return this.totalWidth;
7373     },
7374
7375     /**
7376      * Returns the header for the specified column.
7377      * @param {Number} col The column index
7378      * @return {String}
7379      */
7380     getColumnHeader : function(col){
7381         return this.config[col].header;
7382     },
7383
7384     /**
7385      * Sets the header for a column.
7386      * @param {Number} col The column index
7387      * @param {String} header The new header
7388      */
7389     setColumnHeader : function(col, header){
7390         this.config[col].header = header;
7391         this.fireEvent("headerchange", this, col, header);
7392     },
7393
7394     /**
7395      * Returns the tooltip for the specified column.
7396      * @param {Number} col The column index
7397      * @return {String}
7398      */
7399     getColumnTooltip : function(col){
7400             return this.config[col].tooltip;
7401     },
7402     /**
7403      * Sets the tooltip for a column.
7404      * @param {Number} col The column index
7405      * @param {String} tooltip The new tooltip
7406      */
7407     setColumnTooltip : function(col, tooltip){
7408             this.config[col].tooltip = tooltip;
7409     },
7410
7411     /**
7412      * Returns the dataIndex for the specified column.
7413      * @param {Number} col The column index
7414      * @return {Number}
7415      */
7416     getDataIndex : function(col){
7417         return this.config[col].dataIndex;
7418     },
7419
7420     /**
7421      * Sets the dataIndex for a column.
7422      * @param {Number} col The column index
7423      * @param {Number} dataIndex The new dataIndex
7424      */
7425     setDataIndex : function(col, dataIndex){
7426         this.config[col].dataIndex = dataIndex;
7427     },
7428
7429     
7430     
7431     /**
7432      * Returns true if the cell is editable.
7433      * @param {Number} colIndex The column index
7434      * @param {Number} rowIndex The row index - this is nto actually used..?
7435      * @return {Boolean}
7436      */
7437     isCellEditable : function(colIndex, rowIndex){
7438         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7439     },
7440
7441     /**
7442      * Returns the editor defined for the cell/column.
7443      * return false or null to disable editing.
7444      * @param {Number} colIndex The column index
7445      * @param {Number} rowIndex The row index
7446      * @return {Object}
7447      */
7448     getCellEditor : function(colIndex, rowIndex){
7449         return this.config[colIndex].editor;
7450     },
7451
7452     /**
7453      * Sets if a column is editable.
7454      * @param {Number} col The column index
7455      * @param {Boolean} editable True if the column is editable
7456      */
7457     setEditable : function(col, editable){
7458         this.config[col].editable = editable;
7459     },
7460
7461
7462     /**
7463      * Returns true if the column is hidden.
7464      * @param {Number} colIndex The column index
7465      * @return {Boolean}
7466      */
7467     isHidden : function(colIndex){
7468         return this.config[colIndex].hidden;
7469     },
7470
7471
7472     /**
7473      * Returns true if the column width cannot be changed
7474      */
7475     isFixed : function(colIndex){
7476         return this.config[colIndex].fixed;
7477     },
7478
7479     /**
7480      * Returns true if the column can be resized
7481      * @return {Boolean}
7482      */
7483     isResizable : function(colIndex){
7484         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7485     },
7486     /**
7487      * Sets if a column is hidden.
7488      * @param {Number} colIndex The column index
7489      * @param {Boolean} hidden True if the column is hidden
7490      */
7491     setHidden : function(colIndex, hidden){
7492         this.config[colIndex].hidden = hidden;
7493         this.totalWidth = null;
7494         this.fireEvent("hiddenchange", this, colIndex, hidden);
7495     },
7496
7497     /**
7498      * Sets the editor for a column.
7499      * @param {Number} col The column index
7500      * @param {Object} editor The editor object
7501      */
7502     setEditor : function(col, editor){
7503         this.config[col].editor = editor;
7504     }
7505 });
7506
7507 Roo.grid.ColumnModel.defaultRenderer = function(value)
7508 {
7509     if(typeof value == "object") {
7510         return value;
7511     }
7512         if(typeof value == "string" && value.length < 1){
7513             return "&#160;";
7514         }
7515     
7516         return String.format("{0}", value);
7517 };
7518
7519 // Alias for backwards compatibility
7520 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7521 /*
7522  * Based on:
7523  * Ext JS Library 1.1.1
7524  * Copyright(c) 2006-2007, Ext JS, LLC.
7525  *
7526  * Originally Released Under LGPL - original licence link has changed is not relivant.
7527  *
7528  * Fork - LGPL
7529  * <script type="text/javascript">
7530  */
7531  
7532 /**
7533  * @class Roo.LoadMask
7534  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7535  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7536  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7537  * element's UpdateManager load indicator and will be destroyed after the initial load.
7538  * @constructor
7539  * Create a new LoadMask
7540  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7541  * @param {Object} config The config object
7542  */
7543 Roo.LoadMask = function(el, config){
7544     this.el = Roo.get(el);
7545     Roo.apply(this, config);
7546     if(this.store){
7547         this.store.on('beforeload', this.onBeforeLoad, this);
7548         this.store.on('load', this.onLoad, this);
7549         this.store.on('loadexception', this.onLoadException, this);
7550         this.removeMask = false;
7551     }else{
7552         var um = this.el.getUpdateManager();
7553         um.showLoadIndicator = false; // disable the default indicator
7554         um.on('beforeupdate', this.onBeforeLoad, this);
7555         um.on('update', this.onLoad, this);
7556         um.on('failure', this.onLoad, this);
7557         this.removeMask = true;
7558     }
7559 };
7560
7561 Roo.LoadMask.prototype = {
7562     /**
7563      * @cfg {Boolean} removeMask
7564      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7565      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7566      */
7567     /**
7568      * @cfg {String} msg
7569      * The text to display in a centered loading message box (defaults to 'Loading...')
7570      */
7571     msg : 'Loading...',
7572     /**
7573      * @cfg {String} msgCls
7574      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7575      */
7576     msgCls : 'x-mask-loading',
7577
7578     /**
7579      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7580      * @type Boolean
7581      */
7582     disabled: false,
7583
7584     /**
7585      * Disables the mask to prevent it from being displayed
7586      */
7587     disable : function(){
7588        this.disabled = true;
7589     },
7590
7591     /**
7592      * Enables the mask so that it can be displayed
7593      */
7594     enable : function(){
7595         this.disabled = false;
7596     },
7597     
7598     onLoadException : function()
7599     {
7600         Roo.log(arguments);
7601         
7602         if (typeof(arguments[3]) != 'undefined') {
7603             Roo.MessageBox.alert("Error loading",arguments[3]);
7604         } 
7605         /*
7606         try {
7607             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7608                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7609             }   
7610         } catch(e) {
7611             
7612         }
7613         */
7614     
7615         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7616     },
7617     // private
7618     onLoad : function()
7619     {
7620         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7621     },
7622
7623     // private
7624     onBeforeLoad : function(){
7625         if(!this.disabled){
7626             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7627         }
7628     },
7629
7630     // private
7631     destroy : function(){
7632         if(this.store){
7633             this.store.un('beforeload', this.onBeforeLoad, this);
7634             this.store.un('load', this.onLoad, this);
7635             this.store.un('loadexception', this.onLoadException, this);
7636         }else{
7637             var um = this.el.getUpdateManager();
7638             um.un('beforeupdate', this.onBeforeLoad, this);
7639             um.un('update', this.onLoad, this);
7640             um.un('failure', this.onLoad, this);
7641         }
7642     }
7643 };/*
7644  * - LGPL
7645  *
7646  * table
7647  * 
7648  */
7649
7650 /**
7651  * @class Roo.bootstrap.Table
7652  * @extends Roo.bootstrap.Component
7653  * Bootstrap Table class
7654  * @cfg {String} cls table class
7655  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7656  * @cfg {String} bgcolor Specifies the background color for a table
7657  * @cfg {Number} border Specifies whether the table cells should have borders or not
7658  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7659  * @cfg {Number} cellspacing Specifies the space between cells
7660  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7661  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7662  * @cfg {String} sortable Specifies that the table should be sortable
7663  * @cfg {String} summary Specifies a summary of the content of a table
7664  * @cfg {Number} width Specifies the width of a table
7665  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7666  * 
7667  * @cfg {boolean} striped Should the rows be alternative striped
7668  * @cfg {boolean} bordered Add borders to the table
7669  * @cfg {boolean} hover Add hover highlighting
7670  * @cfg {boolean} condensed Format condensed
7671  * @cfg {boolean} responsive Format condensed
7672  * @cfg {Boolean} loadMask (true|false) default false
7673  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7674  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7675  * @cfg {Boolean} rowSelection (true|false) default false
7676  * @cfg {Boolean} cellSelection (true|false) default false
7677  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7678  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7679  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7680  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7681  
7682  * 
7683  * @constructor
7684  * Create a new Table
7685  * @param {Object} config The config object
7686  */
7687
7688 Roo.bootstrap.Table = function(config){
7689     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7690     
7691   
7692     
7693     // BC...
7694     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7695     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7696     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7697     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7698     
7699     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7700     if (this.sm) {
7701         this.sm.grid = this;
7702         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7703         this.sm = this.selModel;
7704         this.sm.xmodule = this.xmodule || false;
7705     }
7706     
7707     if (this.cm && typeof(this.cm.config) == 'undefined') {
7708         this.colModel = new Roo.grid.ColumnModel(this.cm);
7709         this.cm = this.colModel;
7710         this.cm.xmodule = this.xmodule || false;
7711     }
7712     if (this.store) {
7713         this.store= Roo.factory(this.store, Roo.data);
7714         this.ds = this.store;
7715         this.ds.xmodule = this.xmodule || false;
7716          
7717     }
7718     if (this.footer && this.store) {
7719         this.footer.dataSource = this.ds;
7720         this.footer = Roo.factory(this.footer);
7721     }
7722     
7723     /** @private */
7724     this.addEvents({
7725         /**
7726          * @event cellclick
7727          * Fires when a cell is clicked
7728          * @param {Roo.bootstrap.Table} this
7729          * @param {Roo.Element} el
7730          * @param {Number} rowIndex
7731          * @param {Number} columnIndex
7732          * @param {Roo.EventObject} e
7733          */
7734         "cellclick" : true,
7735         /**
7736          * @event celldblclick
7737          * Fires when a cell is double clicked
7738          * @param {Roo.bootstrap.Table} this
7739          * @param {Roo.Element} el
7740          * @param {Number} rowIndex
7741          * @param {Number} columnIndex
7742          * @param {Roo.EventObject} e
7743          */
7744         "celldblclick" : true,
7745         /**
7746          * @event rowclick
7747          * Fires when a row is clicked
7748          * @param {Roo.bootstrap.Table} this
7749          * @param {Roo.Element} el
7750          * @param {Number} rowIndex
7751          * @param {Roo.EventObject} e
7752          */
7753         "rowclick" : true,
7754         /**
7755          * @event rowdblclick
7756          * Fires when a row is double clicked
7757          * @param {Roo.bootstrap.Table} this
7758          * @param {Roo.Element} el
7759          * @param {Number} rowIndex
7760          * @param {Roo.EventObject} e
7761          */
7762         "rowdblclick" : true,
7763         /**
7764          * @event mouseover
7765          * Fires when a mouseover occur
7766          * @param {Roo.bootstrap.Table} this
7767          * @param {Roo.Element} el
7768          * @param {Number} rowIndex
7769          * @param {Number} columnIndex
7770          * @param {Roo.EventObject} e
7771          */
7772         "mouseover" : true,
7773         /**
7774          * @event mouseout
7775          * Fires when a mouseout occur
7776          * @param {Roo.bootstrap.Table} this
7777          * @param {Roo.Element} el
7778          * @param {Number} rowIndex
7779          * @param {Number} columnIndex
7780          * @param {Roo.EventObject} e
7781          */
7782         "mouseout" : true,
7783         /**
7784          * @event rowclass
7785          * Fires when a row is rendered, so you can change add a style to it.
7786          * @param {Roo.bootstrap.Table} this
7787          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
7788          */
7789         'rowclass' : true,
7790           /**
7791          * @event rowsrendered
7792          * Fires when all the  rows have been rendered
7793          * @param {Roo.bootstrap.Table} this
7794          */
7795         'rowsrendered' : true,
7796         /**
7797          * @event contextmenu
7798          * The raw contextmenu event for the entire grid.
7799          * @param {Roo.EventObject} e
7800          */
7801         "contextmenu" : true,
7802         /**
7803          * @event rowcontextmenu
7804          * Fires when a row is right clicked
7805          * @param {Roo.bootstrap.Table} this
7806          * @param {Number} rowIndex
7807          * @param {Roo.EventObject} e
7808          */
7809         "rowcontextmenu" : true,
7810         /**
7811          * @event cellcontextmenu
7812          * Fires when a cell is right clicked
7813          * @param {Roo.bootstrap.Table} this
7814          * @param {Number} rowIndex
7815          * @param {Number} cellIndex
7816          * @param {Roo.EventObject} e
7817          */
7818          "cellcontextmenu" : true,
7819          /**
7820          * @event headercontextmenu
7821          * Fires when a header is right clicked
7822          * @param {Roo.bootstrap.Table} this
7823          * @param {Number} columnIndex
7824          * @param {Roo.EventObject} e
7825          */
7826         "headercontextmenu" : true
7827     });
7828 };
7829
7830 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
7831     
7832     cls: false,
7833     align: false,
7834     bgcolor: false,
7835     border: false,
7836     cellpadding: false,
7837     cellspacing: false,
7838     frame: false,
7839     rules: false,
7840     sortable: false,
7841     summary: false,
7842     width: false,
7843     striped : false,
7844     scrollBody : false,
7845     bordered: false,
7846     hover:  false,
7847     condensed : false,
7848     responsive : false,
7849     sm : false,
7850     cm : false,
7851     store : false,
7852     loadMask : false,
7853     footerShow : true,
7854     headerShow : true,
7855   
7856     rowSelection : false,
7857     cellSelection : false,
7858     layout : false,
7859     
7860     // Roo.Element - the tbody
7861     mainBody: false,
7862     // Roo.Element - thead element
7863     mainHead: false,
7864     
7865     container: false, // used by gridpanel...
7866     
7867     lazyLoad : false,
7868     
7869     CSS : Roo.util.CSS,
7870     
7871     auto_hide_footer : false,
7872     
7873     getAutoCreate : function()
7874     {
7875         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7876         
7877         cfg = {
7878             tag: 'table',
7879             cls : 'table',
7880             cn : []
7881         };
7882         if (this.scrollBody) {
7883             cfg.cls += ' table-body-fixed';
7884         }    
7885         if (this.striped) {
7886             cfg.cls += ' table-striped';
7887         }
7888         
7889         if (this.hover) {
7890             cfg.cls += ' table-hover';
7891         }
7892         if (this.bordered) {
7893             cfg.cls += ' table-bordered';
7894         }
7895         if (this.condensed) {
7896             cfg.cls += ' table-condensed';
7897         }
7898         if (this.responsive) {
7899             cfg.cls += ' table-responsive';
7900         }
7901         
7902         if (this.cls) {
7903             cfg.cls+=  ' ' +this.cls;
7904         }
7905         
7906         // this lot should be simplifed...
7907         var _t = this;
7908         var cp = [
7909             'align',
7910             'bgcolor',
7911             'border',
7912             'cellpadding',
7913             'cellspacing',
7914             'frame',
7915             'rules',
7916             'sortable',
7917             'summary',
7918             'width'
7919         ].forEach(function(k) {
7920             if (_t[k]) {
7921                 cfg[k] = _t[k];
7922             }
7923         });
7924         
7925         
7926         if (this.layout) {
7927             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7928         }
7929         
7930         if(this.store || this.cm){
7931             if(this.headerShow){
7932                 cfg.cn.push(this.renderHeader());
7933             }
7934             
7935             cfg.cn.push(this.renderBody());
7936             
7937             if(this.footerShow){
7938                 cfg.cn.push(this.renderFooter());
7939             }
7940             // where does this come from?
7941             //cfg.cls+=  ' TableGrid';
7942         }
7943         
7944         return { cn : [ cfg ] };
7945     },
7946     
7947     initEvents : function()
7948     {   
7949         if(!this.store || !this.cm){
7950             return;
7951         }
7952         if (this.selModel) {
7953             this.selModel.initEvents();
7954         }
7955         
7956         
7957         //Roo.log('initEvents with ds!!!!');
7958         
7959         this.mainBody = this.el.select('tbody', true).first();
7960         this.mainHead = this.el.select('thead', true).first();
7961         this.mainFoot = this.el.select('tfoot', true).first();
7962         
7963         
7964         
7965         var _this = this;
7966         
7967         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7968             e.on('click', _this.sort, _this);
7969         });
7970         
7971         this.mainBody.on("click", this.onClick, this);
7972         this.mainBody.on("dblclick", this.onDblClick, this);
7973         
7974         // why is this done????? = it breaks dialogs??
7975         //this.parent().el.setStyle('position', 'relative');
7976         
7977         
7978         if (this.footer) {
7979             this.footer.parentId = this.id;
7980             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7981             
7982             if(this.lazyLoad){
7983                 this.el.select('tfoot tr td').first().addClass('hide');
7984             }
7985         } 
7986         
7987         if(this.loadMask) {
7988             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7989         }
7990         
7991         this.store.on('load', this.onLoad, this);
7992         this.store.on('beforeload', this.onBeforeLoad, this);
7993         this.store.on('update', this.onUpdate, this);
7994         this.store.on('add', this.onAdd, this);
7995         this.store.on("clear", this.clear, this);
7996         
7997         this.el.on("contextmenu", this.onContextMenu, this);
7998         
7999         this.mainBody.on('scroll', this.onBodyScroll, this);
8000         
8001         this.cm.on("headerchange", this.onHeaderChange, this);
8002         
8003         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8004         
8005     },
8006     
8007     onContextMenu : function(e, t)
8008     {
8009         this.processEvent("contextmenu", e);
8010     },
8011     
8012     processEvent : function(name, e)
8013     {
8014         if (name != 'touchstart' ) {
8015             this.fireEvent(name, e);    
8016         }
8017         
8018         var t = e.getTarget();
8019         
8020         var cell = Roo.get(t);
8021         
8022         if(!cell){
8023             return;
8024         }
8025         
8026         if(cell.findParent('tfoot', false, true)){
8027             return;
8028         }
8029         
8030         if(cell.findParent('thead', false, true)){
8031             
8032             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8033                 cell = Roo.get(t).findParent('th', false, true);
8034                 if (!cell) {
8035                     Roo.log("failed to find th in thead?");
8036                     Roo.log(e.getTarget());
8037                     return;
8038                 }
8039             }
8040             
8041             var cellIndex = cell.dom.cellIndex;
8042             
8043             var ename = name == 'touchstart' ? 'click' : name;
8044             this.fireEvent("header" + ename, this, cellIndex, e);
8045             
8046             return;
8047         }
8048         
8049         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8050             cell = Roo.get(t).findParent('td', false, true);
8051             if (!cell) {
8052                 Roo.log("failed to find th in tbody?");
8053                 Roo.log(e.getTarget());
8054                 return;
8055             }
8056         }
8057         
8058         var row = cell.findParent('tr', false, true);
8059         var cellIndex = cell.dom.cellIndex;
8060         var rowIndex = row.dom.rowIndex - 1;
8061         
8062         if(row !== false){
8063             
8064             this.fireEvent("row" + name, this, rowIndex, e);
8065             
8066             if(cell !== false){
8067             
8068                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8069             }
8070         }
8071         
8072     },
8073     
8074     onMouseover : function(e, el)
8075     {
8076         var cell = Roo.get(el);
8077         
8078         if(!cell){
8079             return;
8080         }
8081         
8082         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8083             cell = cell.findParent('td', false, true);
8084         }
8085         
8086         var row = cell.findParent('tr', false, true);
8087         var cellIndex = cell.dom.cellIndex;
8088         var rowIndex = row.dom.rowIndex - 1; // start from 0
8089         
8090         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8091         
8092     },
8093     
8094     onMouseout : function(e, el)
8095     {
8096         var cell = Roo.get(el);
8097         
8098         if(!cell){
8099             return;
8100         }
8101         
8102         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8103             cell = cell.findParent('td', false, true);
8104         }
8105         
8106         var row = cell.findParent('tr', false, true);
8107         var cellIndex = cell.dom.cellIndex;
8108         var rowIndex = row.dom.rowIndex - 1; // start from 0
8109         
8110         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8111         
8112     },
8113     
8114     onClick : function(e, el)
8115     {
8116         var cell = Roo.get(el);
8117         
8118         if(!cell || (!this.cellSelection && !this.rowSelection)){
8119             return;
8120         }
8121         
8122         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8123             cell = cell.findParent('td', false, true);
8124         }
8125         
8126         if(!cell || typeof(cell) == 'undefined'){
8127             return;
8128         }
8129         
8130         var row = cell.findParent('tr', false, true);
8131         
8132         if(!row || typeof(row) == 'undefined'){
8133             return;
8134         }
8135         
8136         var cellIndex = cell.dom.cellIndex;
8137         var rowIndex = this.getRowIndex(row);
8138         
8139         // why??? - should these not be based on SelectionModel?
8140         if(this.cellSelection){
8141             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8142         }
8143         
8144         if(this.rowSelection){
8145             this.fireEvent('rowclick', this, row, rowIndex, e);
8146         }
8147         
8148         
8149     },
8150         
8151     onDblClick : function(e,el)
8152     {
8153         var cell = Roo.get(el);
8154         
8155         if(!cell || (!this.cellSelection && !this.rowSelection)){
8156             return;
8157         }
8158         
8159         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8160             cell = cell.findParent('td', false, true);
8161         }
8162         
8163         if(!cell || typeof(cell) == 'undefined'){
8164             return;
8165         }
8166         
8167         var row = cell.findParent('tr', false, true);
8168         
8169         if(!row || typeof(row) == 'undefined'){
8170             return;
8171         }
8172         
8173         var cellIndex = cell.dom.cellIndex;
8174         var rowIndex = this.getRowIndex(row);
8175         
8176         if(this.cellSelection){
8177             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8178         }
8179         
8180         if(this.rowSelection){
8181             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8182         }
8183     },
8184     
8185     sort : function(e,el)
8186     {
8187         var col = Roo.get(el);
8188         
8189         if(!col.hasClass('sortable')){
8190             return;
8191         }
8192         
8193         var sort = col.attr('sort');
8194         var dir = 'ASC';
8195         
8196         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8197             dir = 'DESC';
8198         }
8199         
8200         this.store.sortInfo = {field : sort, direction : dir};
8201         
8202         if (this.footer) {
8203             Roo.log("calling footer first");
8204             this.footer.onClick('first');
8205         } else {
8206         
8207             this.store.load({ params : { start : 0 } });
8208         }
8209     },
8210     
8211     renderHeader : function()
8212     {
8213         var header = {
8214             tag: 'thead',
8215             cn : []
8216         };
8217         
8218         var cm = this.cm;
8219         this.totalWidth = 0;
8220         
8221         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8222             
8223             var config = cm.config[i];
8224             
8225             var c = {
8226                 tag: 'th',
8227                 cls : 'x-hcol-' + i,
8228                 style : '',
8229                 html: cm.getColumnHeader(i)
8230             };
8231             
8232             var hh = '';
8233             
8234             if(typeof(config.sortable) != 'undefined' && config.sortable){
8235                 c.cls = 'sortable';
8236                 c.html = '<i class="glyphicon"></i>' + c.html;
8237             }
8238             
8239             // could use BS4 hidden-..-down 
8240             
8241             if(typeof(config.lgHeader) != 'undefined'){
8242                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8243             }
8244             
8245             if(typeof(config.mdHeader) != 'undefined'){
8246                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8247             }
8248             
8249             if(typeof(config.smHeader) != 'undefined'){
8250                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8251             }
8252             
8253             if(typeof(config.xsHeader) != 'undefined'){
8254                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8255             }
8256             
8257             if(hh.length){
8258                 c.html = hh;
8259             }
8260             
8261             if(typeof(config.tooltip) != 'undefined'){
8262                 c.tooltip = config.tooltip;
8263             }
8264             
8265             if(typeof(config.colspan) != 'undefined'){
8266                 c.colspan = config.colspan;
8267             }
8268             
8269             if(typeof(config.hidden) != 'undefined' && config.hidden){
8270                 c.style += ' display:none;';
8271             }
8272             
8273             if(typeof(config.dataIndex) != 'undefined'){
8274                 c.sort = config.dataIndex;
8275             }
8276             
8277            
8278             
8279             if(typeof(config.align) != 'undefined' && config.align.length){
8280                 c.style += ' text-align:' + config.align + ';';
8281             }
8282             
8283             if(typeof(config.width) != 'undefined'){
8284                 c.style += ' width:' + config.width + 'px;';
8285                 this.totalWidth += config.width;
8286             } else {
8287                 this.totalWidth += 100; // assume minimum of 100 per column?
8288             }
8289             
8290             if(typeof(config.cls) != 'undefined'){
8291                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8292             }
8293             
8294             ['xs','sm','md','lg'].map(function(size){
8295                 
8296                 if(typeof(config[size]) == 'undefined'){
8297                     return;
8298                 }
8299                  
8300                 if (!config[size]) { // 0 = hidden
8301                     // BS 4 '0' is treated as hide that column and below.
8302                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8303                     return;
8304                 }
8305                 
8306                 c.cls += ' col-' + size + '-' + config[size] + (
8307                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8308                 );
8309                 
8310                 
8311             });
8312             
8313             header.cn.push(c)
8314         }
8315         
8316         return header;
8317     },
8318     
8319     renderBody : function()
8320     {
8321         var body = {
8322             tag: 'tbody',
8323             cn : [
8324                 {
8325                     tag: 'tr',
8326                     cn : [
8327                         {
8328                             tag : 'td',
8329                             colspan :  this.cm.getColumnCount()
8330                         }
8331                     ]
8332                 }
8333             ]
8334         };
8335         
8336         return body;
8337     },
8338     
8339     renderFooter : function()
8340     {
8341         var footer = {
8342             tag: 'tfoot',
8343             cn : [
8344                 {
8345                     tag: 'tr',
8346                     cn : [
8347                         {
8348                             tag : 'td',
8349                             colspan :  this.cm.getColumnCount()
8350                         }
8351                     ]
8352                 }
8353             ]
8354         };
8355         
8356         return footer;
8357     },
8358     
8359     
8360     
8361     onLoad : function()
8362     {
8363 //        Roo.log('ds onload');
8364         this.clear();
8365         
8366         var _this = this;
8367         var cm = this.cm;
8368         var ds = this.store;
8369         
8370         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8371             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8372             if (_this.store.sortInfo) {
8373                     
8374                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8375                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8376                 }
8377                 
8378                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8379                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8380                 }
8381             }
8382         });
8383         
8384         var tbody =  this.mainBody;
8385               
8386         if(ds.getCount() > 0){
8387             ds.data.each(function(d,rowIndex){
8388                 var row =  this.renderRow(cm, ds, rowIndex);
8389                 
8390                 tbody.createChild(row);
8391                 
8392                 var _this = this;
8393                 
8394                 if(row.cellObjects.length){
8395                     Roo.each(row.cellObjects, function(r){
8396                         _this.renderCellObject(r);
8397                     })
8398                 }
8399                 
8400             }, this);
8401         }
8402         
8403         var tfoot = this.el.select('tfoot', true).first();
8404         
8405         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8406             
8407             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8408             
8409             var total = this.ds.getTotalCount();
8410             
8411             if(this.footer.pageSize < total){
8412                 this.mainFoot.show();
8413             }
8414         }
8415         
8416         Roo.each(this.el.select('tbody td', true).elements, function(e){
8417             e.on('mouseover', _this.onMouseover, _this);
8418         });
8419         
8420         Roo.each(this.el.select('tbody td', true).elements, function(e){
8421             e.on('mouseout', _this.onMouseout, _this);
8422         });
8423         this.fireEvent('rowsrendered', this);
8424         
8425         this.autoSize();
8426     },
8427     
8428     
8429     onUpdate : function(ds,record)
8430     {
8431         this.refreshRow(record);
8432         this.autoSize();
8433     },
8434     
8435     onRemove : function(ds, record, index, isUpdate){
8436         if(isUpdate !== true){
8437             this.fireEvent("beforerowremoved", this, index, record);
8438         }
8439         var bt = this.mainBody.dom;
8440         
8441         var rows = this.el.select('tbody > tr', true).elements;
8442         
8443         if(typeof(rows[index]) != 'undefined'){
8444             bt.removeChild(rows[index].dom);
8445         }
8446         
8447 //        if(bt.rows[index]){
8448 //            bt.removeChild(bt.rows[index]);
8449 //        }
8450         
8451         if(isUpdate !== true){
8452             //this.stripeRows(index);
8453             //this.syncRowHeights(index, index);
8454             //this.layout();
8455             this.fireEvent("rowremoved", this, index, record);
8456         }
8457     },
8458     
8459     onAdd : function(ds, records, rowIndex)
8460     {
8461         //Roo.log('on Add called');
8462         // - note this does not handle multiple adding very well..
8463         var bt = this.mainBody.dom;
8464         for (var i =0 ; i < records.length;i++) {
8465             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8466             //Roo.log(records[i]);
8467             //Roo.log(this.store.getAt(rowIndex+i));
8468             this.insertRow(this.store, rowIndex + i, false);
8469             return;
8470         }
8471         
8472     },
8473     
8474     
8475     refreshRow : function(record){
8476         var ds = this.store, index;
8477         if(typeof record == 'number'){
8478             index = record;
8479             record = ds.getAt(index);
8480         }else{
8481             index = ds.indexOf(record);
8482             if (index < 0) {
8483                 return; // should not happen - but seems to 
8484             }
8485         }
8486         this.insertRow(ds, index, true);
8487         this.autoSize();
8488         this.onRemove(ds, record, index+1, true);
8489         this.autoSize();
8490         //this.syncRowHeights(index, index);
8491         //this.layout();
8492         this.fireEvent("rowupdated", this, index, record);
8493     },
8494     
8495     insertRow : function(dm, rowIndex, isUpdate){
8496         
8497         if(!isUpdate){
8498             this.fireEvent("beforerowsinserted", this, rowIndex);
8499         }
8500             //var s = this.getScrollState();
8501         var row = this.renderRow(this.cm, this.store, rowIndex);
8502         // insert before rowIndex..
8503         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8504         
8505         var _this = this;
8506                 
8507         if(row.cellObjects.length){
8508             Roo.each(row.cellObjects, function(r){
8509                 _this.renderCellObject(r);
8510             })
8511         }
8512             
8513         if(!isUpdate){
8514             this.fireEvent("rowsinserted", this, rowIndex);
8515             //this.syncRowHeights(firstRow, lastRow);
8516             //this.stripeRows(firstRow);
8517             //this.layout();
8518         }
8519         
8520     },
8521     
8522     
8523     getRowDom : function(rowIndex)
8524     {
8525         var rows = this.el.select('tbody > tr', true).elements;
8526         
8527         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8528         
8529     },
8530     // returns the object tree for a tr..
8531   
8532     
8533     renderRow : function(cm, ds, rowIndex) 
8534     {
8535         var d = ds.getAt(rowIndex);
8536         
8537         var row = {
8538             tag : 'tr',
8539             cls : 'x-row-' + rowIndex,
8540             cn : []
8541         };
8542             
8543         var cellObjects = [];
8544         
8545         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8546             var config = cm.config[i];
8547             
8548             var renderer = cm.getRenderer(i);
8549             var value = '';
8550             var id = false;
8551             
8552             if(typeof(renderer) !== 'undefined'){
8553                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8554             }
8555             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8556             // and are rendered into the cells after the row is rendered - using the id for the element.
8557             
8558             if(typeof(value) === 'object'){
8559                 id = Roo.id();
8560                 cellObjects.push({
8561                     container : id,
8562                     cfg : value 
8563                 })
8564             }
8565             
8566             var rowcfg = {
8567                 record: d,
8568                 rowIndex : rowIndex,
8569                 colIndex : i,
8570                 rowClass : ''
8571             };
8572
8573             this.fireEvent('rowclass', this, rowcfg);
8574             
8575             var td = {
8576                 tag: 'td',
8577                 cls : rowcfg.rowClass + ' x-col-' + i,
8578                 style: '',
8579                 html: (typeof(value) === 'object') ? '' : value
8580             };
8581             
8582             if (id) {
8583                 td.id = id;
8584             }
8585             
8586             if(typeof(config.colspan) != 'undefined'){
8587                 td.colspan = config.colspan;
8588             }
8589             
8590             if(typeof(config.hidden) != 'undefined' && config.hidden){
8591                 td.style += ' display:none;';
8592             }
8593             
8594             if(typeof(config.align) != 'undefined' && config.align.length){
8595                 td.style += ' text-align:' + config.align + ';';
8596             }
8597             if(typeof(config.valign) != 'undefined' && config.valign.length){
8598                 td.style += ' vertical-align:' + config.valign + ';';
8599             }
8600             
8601             if(typeof(config.width) != 'undefined'){
8602                 td.style += ' width:' +  config.width + 'px;';
8603             }
8604             
8605             if(typeof(config.cursor) != 'undefined'){
8606                 td.style += ' cursor:' +  config.cursor + ';';
8607             }
8608             
8609             if(typeof(config.cls) != 'undefined'){
8610                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8611             }
8612             
8613             ['xs','sm','md','lg'].map(function(size){
8614                 
8615                 if(typeof(config[size]) == 'undefined'){
8616                     return;
8617                 }
8618                 
8619                 
8620                   
8621                 if (!config[size]) { // 0 = hidden
8622                     // BS 4 '0' is treated as hide that column and below.
8623                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8624                     return;
8625                 }
8626                 
8627                 td.cls += ' col-' + size + '-' + config[size] + (
8628                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8629                 );
8630                  
8631
8632             });
8633             
8634             row.cn.push(td);
8635            
8636         }
8637         
8638         row.cellObjects = cellObjects;
8639         
8640         return row;
8641           
8642     },
8643     
8644     
8645     
8646     onBeforeLoad : function()
8647     {
8648         
8649     },
8650      /**
8651      * Remove all rows
8652      */
8653     clear : function()
8654     {
8655         this.el.select('tbody', true).first().dom.innerHTML = '';
8656     },
8657     /**
8658      * Show or hide a row.
8659      * @param {Number} rowIndex to show or hide
8660      * @param {Boolean} state hide
8661      */
8662     setRowVisibility : function(rowIndex, state)
8663     {
8664         var bt = this.mainBody.dom;
8665         
8666         var rows = this.el.select('tbody > tr', true).elements;
8667         
8668         if(typeof(rows[rowIndex]) == 'undefined'){
8669             return;
8670         }
8671         rows[rowIndex].dom.style.display = state ? '' : 'none';
8672     },
8673     
8674     
8675     getSelectionModel : function(){
8676         if(!this.selModel){
8677             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8678         }
8679         return this.selModel;
8680     },
8681     /*
8682      * Render the Roo.bootstrap object from renderder
8683      */
8684     renderCellObject : function(r)
8685     {
8686         var _this = this;
8687         
8688         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8689         
8690         var t = r.cfg.render(r.container);
8691         
8692         if(r.cfg.cn){
8693             Roo.each(r.cfg.cn, function(c){
8694                 var child = {
8695                     container: t.getChildContainer(),
8696                     cfg: c
8697                 };
8698                 _this.renderCellObject(child);
8699             })
8700         }
8701     },
8702     
8703     getRowIndex : function(row)
8704     {
8705         var rowIndex = -1;
8706         
8707         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8708             if(el != row){
8709                 return;
8710             }
8711             
8712             rowIndex = index;
8713         });
8714         
8715         return rowIndex;
8716     },
8717      /**
8718      * Returns the grid's underlying element = used by panel.Grid
8719      * @return {Element} The element
8720      */
8721     getGridEl : function(){
8722         return this.el;
8723     },
8724      /**
8725      * Forces a resize - used by panel.Grid
8726      * @return {Element} The element
8727      */
8728     autoSize : function()
8729     {
8730         //var ctr = Roo.get(this.container.dom.parentElement);
8731         var ctr = Roo.get(this.el.dom);
8732         
8733         var thd = this.getGridEl().select('thead',true).first();
8734         var tbd = this.getGridEl().select('tbody', true).first();
8735         var tfd = this.getGridEl().select('tfoot', true).first();
8736         
8737         var cw = ctr.getWidth();
8738         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
8739         
8740         if (tbd) {
8741             
8742             tbd.setWidth(ctr.getWidth());
8743             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8744             // this needs fixing for various usage - currently only hydra job advers I think..
8745             //tdb.setHeight(
8746             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8747             //); 
8748             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8749             cw -= barsize;
8750         }
8751         cw = Math.max(cw, this.totalWidth);
8752         this.getGridEl().select('tbody tr',true).setWidth(cw);
8753         
8754         // resize 'expandable coloumn?
8755         
8756         return; // we doe not have a view in this design..
8757         
8758     },
8759     onBodyScroll: function()
8760     {
8761         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8762         if(this.mainHead){
8763             this.mainHead.setStyle({
8764                 'position' : 'relative',
8765                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8766             });
8767         }
8768         
8769         if(this.lazyLoad){
8770             
8771             var scrollHeight = this.mainBody.dom.scrollHeight;
8772             
8773             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8774             
8775             var height = this.mainBody.getHeight();
8776             
8777             if(scrollHeight - height == scrollTop) {
8778                 
8779                 var total = this.ds.getTotalCount();
8780                 
8781                 if(this.footer.cursor + this.footer.pageSize < total){
8782                     
8783                     this.footer.ds.load({
8784                         params : {
8785                             start : this.footer.cursor + this.footer.pageSize,
8786                             limit : this.footer.pageSize
8787                         },
8788                         add : true
8789                     });
8790                 }
8791             }
8792             
8793         }
8794     },
8795     
8796     onHeaderChange : function()
8797     {
8798         var header = this.renderHeader();
8799         var table = this.el.select('table', true).first();
8800         
8801         this.mainHead.remove();
8802         this.mainHead = table.createChild(header, this.mainBody, false);
8803     },
8804     
8805     onHiddenChange : function(colModel, colIndex, hidden)
8806     {
8807         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8808         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8809         
8810         this.CSS.updateRule(thSelector, "display", "");
8811         this.CSS.updateRule(tdSelector, "display", "");
8812         
8813         if(hidden){
8814             this.CSS.updateRule(thSelector, "display", "none");
8815             this.CSS.updateRule(tdSelector, "display", "none");
8816         }
8817         
8818         this.onHeaderChange();
8819         this.onLoad();
8820     },
8821     
8822     setColumnWidth: function(col_index, width)
8823     {
8824         // width = "md-2 xs-2..."
8825         if(!this.colModel.config[col_index]) {
8826             return;
8827         }
8828         
8829         var w = width.split(" ");
8830         
8831         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8832         
8833         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8834         
8835         
8836         for(var j = 0; j < w.length; j++) {
8837             
8838             if(!w[j]) {
8839                 continue;
8840             }
8841             
8842             var size_cls = w[j].split("-");
8843             
8844             if(!Number.isInteger(size_cls[1] * 1)) {
8845                 continue;
8846             }
8847             
8848             if(!this.colModel.config[col_index][size_cls[0]]) {
8849                 continue;
8850             }
8851             
8852             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8853                 continue;
8854             }
8855             
8856             h_row[0].classList.replace(
8857                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8858                 "col-"+size_cls[0]+"-"+size_cls[1]
8859             );
8860             
8861             for(var i = 0; i < rows.length; i++) {
8862                 
8863                 var size_cls = w[j].split("-");
8864                 
8865                 if(!Number.isInteger(size_cls[1] * 1)) {
8866                     continue;
8867                 }
8868                 
8869                 if(!this.colModel.config[col_index][size_cls[0]]) {
8870                     continue;
8871                 }
8872                 
8873                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8874                     continue;
8875                 }
8876                 
8877                 rows[i].classList.replace(
8878                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8879                     "col-"+size_cls[0]+"-"+size_cls[1]
8880                 );
8881             }
8882             
8883             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8884         }
8885     }
8886 });
8887
8888  
8889
8890  /*
8891  * - LGPL
8892  *
8893  * table cell
8894  * 
8895  */
8896
8897 /**
8898  * @class Roo.bootstrap.TableCell
8899  * @extends Roo.bootstrap.Component
8900  * Bootstrap TableCell class
8901  * @cfg {String} html cell contain text
8902  * @cfg {String} cls cell class
8903  * @cfg {String} tag cell tag (td|th) default td
8904  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8905  * @cfg {String} align Aligns the content in a cell
8906  * @cfg {String} axis Categorizes cells
8907  * @cfg {String} bgcolor Specifies the background color of a cell
8908  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8909  * @cfg {Number} colspan Specifies the number of columns a cell should span
8910  * @cfg {String} headers Specifies one or more header cells a cell is related to
8911  * @cfg {Number} height Sets the height of a cell
8912  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8913  * @cfg {Number} rowspan Sets the number of rows a cell should span
8914  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8915  * @cfg {String} valign Vertical aligns the content in a cell
8916  * @cfg {Number} width Specifies the width of a cell
8917  * 
8918  * @constructor
8919  * Create a new TableCell
8920  * @param {Object} config The config object
8921  */
8922
8923 Roo.bootstrap.TableCell = function(config){
8924     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8925 };
8926
8927 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
8928     
8929     html: false,
8930     cls: false,
8931     tag: false,
8932     abbr: false,
8933     align: false,
8934     axis: false,
8935     bgcolor: false,
8936     charoff: false,
8937     colspan: false,
8938     headers: false,
8939     height: false,
8940     nowrap: false,
8941     rowspan: false,
8942     scope: false,
8943     valign: false,
8944     width: false,
8945     
8946     
8947     getAutoCreate : function(){
8948         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8949         
8950         cfg = {
8951             tag: 'td'
8952         };
8953         
8954         if(this.tag){
8955             cfg.tag = this.tag;
8956         }
8957         
8958         if (this.html) {
8959             cfg.html=this.html
8960         }
8961         if (this.cls) {
8962             cfg.cls=this.cls
8963         }
8964         if (this.abbr) {
8965             cfg.abbr=this.abbr
8966         }
8967         if (this.align) {
8968             cfg.align=this.align
8969         }
8970         if (this.axis) {
8971             cfg.axis=this.axis
8972         }
8973         if (this.bgcolor) {
8974             cfg.bgcolor=this.bgcolor
8975         }
8976         if (this.charoff) {
8977             cfg.charoff=this.charoff
8978         }
8979         if (this.colspan) {
8980             cfg.colspan=this.colspan
8981         }
8982         if (this.headers) {
8983             cfg.headers=this.headers
8984         }
8985         if (this.height) {
8986             cfg.height=this.height
8987         }
8988         if (this.nowrap) {
8989             cfg.nowrap=this.nowrap
8990         }
8991         if (this.rowspan) {
8992             cfg.rowspan=this.rowspan
8993         }
8994         if (this.scope) {
8995             cfg.scope=this.scope
8996         }
8997         if (this.valign) {
8998             cfg.valign=this.valign
8999         }
9000         if (this.width) {
9001             cfg.width=this.width
9002         }
9003         
9004         
9005         return cfg;
9006     }
9007    
9008 });
9009
9010  
9011
9012  /*
9013  * - LGPL
9014  *
9015  * table row
9016  * 
9017  */
9018
9019 /**
9020  * @class Roo.bootstrap.TableRow
9021  * @extends Roo.bootstrap.Component
9022  * Bootstrap TableRow class
9023  * @cfg {String} cls row class
9024  * @cfg {String} align Aligns the content in a table row
9025  * @cfg {String} bgcolor Specifies a background color for a table row
9026  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9027  * @cfg {String} valign Vertical aligns the content in a table row
9028  * 
9029  * @constructor
9030  * Create a new TableRow
9031  * @param {Object} config The config object
9032  */
9033
9034 Roo.bootstrap.TableRow = function(config){
9035     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9036 };
9037
9038 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9039     
9040     cls: false,
9041     align: false,
9042     bgcolor: false,
9043     charoff: false,
9044     valign: false,
9045     
9046     getAutoCreate : function(){
9047         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9048         
9049         cfg = {
9050             tag: 'tr'
9051         };
9052             
9053         if(this.cls){
9054             cfg.cls = this.cls;
9055         }
9056         if(this.align){
9057             cfg.align = this.align;
9058         }
9059         if(this.bgcolor){
9060             cfg.bgcolor = this.bgcolor;
9061         }
9062         if(this.charoff){
9063             cfg.charoff = this.charoff;
9064         }
9065         if(this.valign){
9066             cfg.valign = this.valign;
9067         }
9068         
9069         return cfg;
9070     }
9071    
9072 });
9073
9074  
9075
9076  /*
9077  * - LGPL
9078  *
9079  * table body
9080  * 
9081  */
9082
9083 /**
9084  * @class Roo.bootstrap.TableBody
9085  * @extends Roo.bootstrap.Component
9086  * Bootstrap TableBody class
9087  * @cfg {String} cls element class
9088  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9089  * @cfg {String} align Aligns the content inside the element
9090  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9091  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9092  * 
9093  * @constructor
9094  * Create a new TableBody
9095  * @param {Object} config The config object
9096  */
9097
9098 Roo.bootstrap.TableBody = function(config){
9099     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9100 };
9101
9102 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9103     
9104     cls: false,
9105     tag: false,
9106     align: false,
9107     charoff: false,
9108     valign: false,
9109     
9110     getAutoCreate : function(){
9111         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9112         
9113         cfg = {
9114             tag: 'tbody'
9115         };
9116             
9117         if (this.cls) {
9118             cfg.cls=this.cls
9119         }
9120         if(this.tag){
9121             cfg.tag = this.tag;
9122         }
9123         
9124         if(this.align){
9125             cfg.align = this.align;
9126         }
9127         if(this.charoff){
9128             cfg.charoff = this.charoff;
9129         }
9130         if(this.valign){
9131             cfg.valign = this.valign;
9132         }
9133         
9134         return cfg;
9135     }
9136     
9137     
9138 //    initEvents : function()
9139 //    {
9140 //        
9141 //        if(!this.store){
9142 //            return;
9143 //        }
9144 //        
9145 //        this.store = Roo.factory(this.store, Roo.data);
9146 //        this.store.on('load', this.onLoad, this);
9147 //        
9148 //        this.store.load();
9149 //        
9150 //    },
9151 //    
9152 //    onLoad: function () 
9153 //    {   
9154 //        this.fireEvent('load', this);
9155 //    }
9156 //    
9157 //   
9158 });
9159
9160  
9161
9162  /*
9163  * Based on:
9164  * Ext JS Library 1.1.1
9165  * Copyright(c) 2006-2007, Ext JS, LLC.
9166  *
9167  * Originally Released Under LGPL - original licence link has changed is not relivant.
9168  *
9169  * Fork - LGPL
9170  * <script type="text/javascript">
9171  */
9172
9173 // as we use this in bootstrap.
9174 Roo.namespace('Roo.form');
9175  /**
9176  * @class Roo.form.Action
9177  * Internal Class used to handle form actions
9178  * @constructor
9179  * @param {Roo.form.BasicForm} el The form element or its id
9180  * @param {Object} config Configuration options
9181  */
9182
9183  
9184  
9185 // define the action interface
9186 Roo.form.Action = function(form, options){
9187     this.form = form;
9188     this.options = options || {};
9189 };
9190 /**
9191  * Client Validation Failed
9192  * @const 
9193  */
9194 Roo.form.Action.CLIENT_INVALID = 'client';
9195 /**
9196  * Server Validation Failed
9197  * @const 
9198  */
9199 Roo.form.Action.SERVER_INVALID = 'server';
9200  /**
9201  * Connect to Server Failed
9202  * @const 
9203  */
9204 Roo.form.Action.CONNECT_FAILURE = 'connect';
9205 /**
9206  * Reading Data from Server Failed
9207  * @const 
9208  */
9209 Roo.form.Action.LOAD_FAILURE = 'load';
9210
9211 Roo.form.Action.prototype = {
9212     type : 'default',
9213     failureType : undefined,
9214     response : undefined,
9215     result : undefined,
9216
9217     // interface method
9218     run : function(options){
9219
9220     },
9221
9222     // interface method
9223     success : function(response){
9224
9225     },
9226
9227     // interface method
9228     handleResponse : function(response){
9229
9230     },
9231
9232     // default connection failure
9233     failure : function(response){
9234         
9235         this.response = response;
9236         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9237         this.form.afterAction(this, false);
9238     },
9239
9240     processResponse : function(response){
9241         this.response = response;
9242         if(!response.responseText){
9243             return true;
9244         }
9245         this.result = this.handleResponse(response);
9246         return this.result;
9247     },
9248
9249     // utility functions used internally
9250     getUrl : function(appendParams){
9251         var url = this.options.url || this.form.url || this.form.el.dom.action;
9252         if(appendParams){
9253             var p = this.getParams();
9254             if(p){
9255                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9256             }
9257         }
9258         return url;
9259     },
9260
9261     getMethod : function(){
9262         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9263     },
9264
9265     getParams : function(){
9266         var bp = this.form.baseParams;
9267         var p = this.options.params;
9268         if(p){
9269             if(typeof p == "object"){
9270                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9271             }else if(typeof p == 'string' && bp){
9272                 p += '&' + Roo.urlEncode(bp);
9273             }
9274         }else if(bp){
9275             p = Roo.urlEncode(bp);
9276         }
9277         return p;
9278     },
9279
9280     createCallback : function(){
9281         return {
9282             success: this.success,
9283             failure: this.failure,
9284             scope: this,
9285             timeout: (this.form.timeout*1000),
9286             upload: this.form.fileUpload ? this.success : undefined
9287         };
9288     }
9289 };
9290
9291 Roo.form.Action.Submit = function(form, options){
9292     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9293 };
9294
9295 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9296     type : 'submit',
9297
9298     haveProgress : false,
9299     uploadComplete : false,
9300     
9301     // uploadProgress indicator.
9302     uploadProgress : function()
9303     {
9304         if (!this.form.progressUrl) {
9305             return;
9306         }
9307         
9308         if (!this.haveProgress) {
9309             Roo.MessageBox.progress("Uploading", "Uploading");
9310         }
9311         if (this.uploadComplete) {
9312            Roo.MessageBox.hide();
9313            return;
9314         }
9315         
9316         this.haveProgress = true;
9317    
9318         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9319         
9320         var c = new Roo.data.Connection();
9321         c.request({
9322             url : this.form.progressUrl,
9323             params: {
9324                 id : uid
9325             },
9326             method: 'GET',
9327             success : function(req){
9328                //console.log(data);
9329                 var rdata = false;
9330                 var edata;
9331                 try  {
9332                    rdata = Roo.decode(req.responseText)
9333                 } catch (e) {
9334                     Roo.log("Invalid data from server..");
9335                     Roo.log(edata);
9336                     return;
9337                 }
9338                 if (!rdata || !rdata.success) {
9339                     Roo.log(rdata);
9340                     Roo.MessageBox.alert(Roo.encode(rdata));
9341                     return;
9342                 }
9343                 var data = rdata.data;
9344                 
9345                 if (this.uploadComplete) {
9346                    Roo.MessageBox.hide();
9347                    return;
9348                 }
9349                    
9350                 if (data){
9351                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9352                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9353                     );
9354                 }
9355                 this.uploadProgress.defer(2000,this);
9356             },
9357        
9358             failure: function(data) {
9359                 Roo.log('progress url failed ');
9360                 Roo.log(data);
9361             },
9362             scope : this
9363         });
9364            
9365     },
9366     
9367     
9368     run : function()
9369     {
9370         // run get Values on the form, so it syncs any secondary forms.
9371         this.form.getValues();
9372         
9373         var o = this.options;
9374         var method = this.getMethod();
9375         var isPost = method == 'POST';
9376         if(o.clientValidation === false || this.form.isValid()){
9377             
9378             if (this.form.progressUrl) {
9379                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9380                     (new Date() * 1) + '' + Math.random());
9381                     
9382             } 
9383             
9384             
9385             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9386                 form:this.form.el.dom,
9387                 url:this.getUrl(!isPost),
9388                 method: method,
9389                 params:isPost ? this.getParams() : null,
9390                 isUpload: this.form.fileUpload,
9391                 formData : this.form.formData
9392             }));
9393             
9394             this.uploadProgress();
9395
9396         }else if (o.clientValidation !== false){ // client validation failed
9397             this.failureType = Roo.form.Action.CLIENT_INVALID;
9398             this.form.afterAction(this, false);
9399         }
9400     },
9401
9402     success : function(response)
9403     {
9404         this.uploadComplete= true;
9405         if (this.haveProgress) {
9406             Roo.MessageBox.hide();
9407         }
9408         
9409         
9410         var result = this.processResponse(response);
9411         if(result === true || result.success){
9412             this.form.afterAction(this, true);
9413             return;
9414         }
9415         if(result.errors){
9416             this.form.markInvalid(result.errors);
9417             this.failureType = Roo.form.Action.SERVER_INVALID;
9418         }
9419         this.form.afterAction(this, false);
9420     },
9421     failure : function(response)
9422     {
9423         this.uploadComplete= true;
9424         if (this.haveProgress) {
9425             Roo.MessageBox.hide();
9426         }
9427         
9428         this.response = response;
9429         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9430         this.form.afterAction(this, false);
9431     },
9432     
9433     handleResponse : function(response){
9434         if(this.form.errorReader){
9435             var rs = this.form.errorReader.read(response);
9436             var errors = [];
9437             if(rs.records){
9438                 for(var i = 0, len = rs.records.length; i < len; i++) {
9439                     var r = rs.records[i];
9440                     errors[i] = r.data;
9441                 }
9442             }
9443             if(errors.length < 1){
9444                 errors = null;
9445             }
9446             return {
9447                 success : rs.success,
9448                 errors : errors
9449             };
9450         }
9451         var ret = false;
9452         try {
9453             ret = Roo.decode(response.responseText);
9454         } catch (e) {
9455             ret = {
9456                 success: false,
9457                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9458                 errors : []
9459             };
9460         }
9461         return ret;
9462         
9463     }
9464 });
9465
9466
9467 Roo.form.Action.Load = function(form, options){
9468     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9469     this.reader = this.form.reader;
9470 };
9471
9472 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9473     type : 'load',
9474
9475     run : function(){
9476         
9477         Roo.Ajax.request(Roo.apply(
9478                 this.createCallback(), {
9479                     method:this.getMethod(),
9480                     url:this.getUrl(false),
9481                     params:this.getParams()
9482         }));
9483     },
9484
9485     success : function(response){
9486         
9487         var result = this.processResponse(response);
9488         if(result === true || !result.success || !result.data){
9489             this.failureType = Roo.form.Action.LOAD_FAILURE;
9490             this.form.afterAction(this, false);
9491             return;
9492         }
9493         this.form.clearInvalid();
9494         this.form.setValues(result.data);
9495         this.form.afterAction(this, true);
9496     },
9497
9498     handleResponse : function(response){
9499         if(this.form.reader){
9500             var rs = this.form.reader.read(response);
9501             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9502             return {
9503                 success : rs.success,
9504                 data : data
9505             };
9506         }
9507         return Roo.decode(response.responseText);
9508     }
9509 });
9510
9511 Roo.form.Action.ACTION_TYPES = {
9512     'load' : Roo.form.Action.Load,
9513     'submit' : Roo.form.Action.Submit
9514 };/*
9515  * - LGPL
9516  *
9517  * form
9518  *
9519  */
9520
9521 /**
9522  * @class Roo.bootstrap.Form
9523  * @extends Roo.bootstrap.Component
9524  * Bootstrap Form class
9525  * @cfg {String} method  GET | POST (default POST)
9526  * @cfg {String} labelAlign top | left (default top)
9527  * @cfg {String} align left  | right - for navbars
9528  * @cfg {Boolean} loadMask load mask when submit (default true)
9529
9530  *
9531  * @constructor
9532  * Create a new Form
9533  * @param {Object} config The config object
9534  */
9535
9536
9537 Roo.bootstrap.Form = function(config){
9538     
9539     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9540     
9541     Roo.bootstrap.Form.popover.apply();
9542     
9543     this.addEvents({
9544         /**
9545          * @event clientvalidation
9546          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9547          * @param {Form} this
9548          * @param {Boolean} valid true if the form has passed client-side validation
9549          */
9550         clientvalidation: true,
9551         /**
9552          * @event beforeaction
9553          * Fires before any action is performed. Return false to cancel the action.
9554          * @param {Form} this
9555          * @param {Action} action The action to be performed
9556          */
9557         beforeaction: true,
9558         /**
9559          * @event actionfailed
9560          * Fires when an action fails.
9561          * @param {Form} this
9562          * @param {Action} action The action that failed
9563          */
9564         actionfailed : true,
9565         /**
9566          * @event actioncomplete
9567          * Fires when an action is completed.
9568          * @param {Form} this
9569          * @param {Action} action The action that completed
9570          */
9571         actioncomplete : true
9572     });
9573 };
9574
9575 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9576
9577      /**
9578      * @cfg {String} method
9579      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9580      */
9581     method : 'POST',
9582     /**
9583      * @cfg {String} url
9584      * The URL to use for form actions if one isn't supplied in the action options.
9585      */
9586     /**
9587      * @cfg {Boolean} fileUpload
9588      * Set to true if this form is a file upload.
9589      */
9590
9591     /**
9592      * @cfg {Object} baseParams
9593      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9594      */
9595
9596     /**
9597      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9598      */
9599     timeout: 30,
9600     /**
9601      * @cfg {Sting} align (left|right) for navbar forms
9602      */
9603     align : 'left',
9604
9605     // private
9606     activeAction : null,
9607
9608     /**
9609      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9610      * element by passing it or its id or mask the form itself by passing in true.
9611      * @type Mixed
9612      */
9613     waitMsgTarget : false,
9614
9615     loadMask : true,
9616     
9617     /**
9618      * @cfg {Boolean} errorMask (true|false) default false
9619      */
9620     errorMask : false,
9621     
9622     /**
9623      * @cfg {Number} maskOffset Default 100
9624      */
9625     maskOffset : 100,
9626     
9627     /**
9628      * @cfg {Boolean} maskBody
9629      */
9630     maskBody : false,
9631
9632     getAutoCreate : function(){
9633
9634         var cfg = {
9635             tag: 'form',
9636             method : this.method || 'POST',
9637             id : this.id || Roo.id(),
9638             cls : ''
9639         };
9640         if (this.parent().xtype.match(/^Nav/)) {
9641             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9642
9643         }
9644
9645         if (this.labelAlign == 'left' ) {
9646             cfg.cls += ' form-horizontal';
9647         }
9648
9649
9650         return cfg;
9651     },
9652     initEvents : function()
9653     {
9654         this.el.on('submit', this.onSubmit, this);
9655         // this was added as random key presses on the form where triggering form submit.
9656         this.el.on('keypress', function(e) {
9657             if (e.getCharCode() != 13) {
9658                 return true;
9659             }
9660             // we might need to allow it for textareas.. and some other items.
9661             // check e.getTarget().
9662
9663             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9664                 return true;
9665             }
9666
9667             Roo.log("keypress blocked");
9668
9669             e.preventDefault();
9670             return false;
9671         });
9672         
9673     },
9674     // private
9675     onSubmit : function(e){
9676         e.stopEvent();
9677     },
9678
9679      /**
9680      * Returns true if client-side validation on the form is successful.
9681      * @return Boolean
9682      */
9683     isValid : function(){
9684         var items = this.getItems();
9685         var valid = true;
9686         var target = false;
9687         
9688         items.each(function(f){
9689             
9690             if(f.validate()){
9691                 return;
9692             }
9693             
9694             Roo.log('invalid field: ' + f.name);
9695             
9696             valid = false;
9697
9698             if(!target && f.el.isVisible(true)){
9699                 target = f;
9700             }
9701            
9702         });
9703         
9704         if(this.errorMask && !valid){
9705             Roo.bootstrap.Form.popover.mask(this, target);
9706         }
9707         
9708         return valid;
9709     },
9710     
9711     /**
9712      * Returns true if any fields in this form have changed since their original load.
9713      * @return Boolean
9714      */
9715     isDirty : function(){
9716         var dirty = false;
9717         var items = this.getItems();
9718         items.each(function(f){
9719            if(f.isDirty()){
9720                dirty = true;
9721                return false;
9722            }
9723            return true;
9724         });
9725         return dirty;
9726     },
9727      /**
9728      * Performs a predefined action (submit or load) or custom actions you define on this form.
9729      * @param {String} actionName The name of the action type
9730      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9731      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9732      * accept other config options):
9733      * <pre>
9734 Property          Type             Description
9735 ----------------  ---------------  ----------------------------------------------------------------------------------
9736 url               String           The url for the action (defaults to the form's url)
9737 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9738 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9739 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9740                                    validate the form on the client (defaults to false)
9741      * </pre>
9742      * @return {BasicForm} this
9743      */
9744     doAction : function(action, options){
9745         if(typeof action == 'string'){
9746             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9747         }
9748         if(this.fireEvent('beforeaction', this, action) !== false){
9749             this.beforeAction(action);
9750             action.run.defer(100, action);
9751         }
9752         return this;
9753     },
9754
9755     // private
9756     beforeAction : function(action){
9757         var o = action.options;
9758         
9759         if(this.loadMask){
9760             
9761             if(this.maskBody){
9762                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9763             } else {
9764                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9765             }
9766         }
9767         // not really supported yet.. ??
9768
9769         //if(this.waitMsgTarget === true){
9770         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9771         //}else if(this.waitMsgTarget){
9772         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9773         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9774         //}else {
9775         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9776        // }
9777
9778     },
9779
9780     // private
9781     afterAction : function(action, success){
9782         this.activeAction = null;
9783         var o = action.options;
9784
9785         if(this.loadMask){
9786             
9787             if(this.maskBody){
9788                 Roo.get(document.body).unmask();
9789             } else {
9790                 this.el.unmask();
9791             }
9792         }
9793         
9794         //if(this.waitMsgTarget === true){
9795 //            this.el.unmask();
9796         //}else if(this.waitMsgTarget){
9797         //    this.waitMsgTarget.unmask();
9798         //}else{
9799         //    Roo.MessageBox.updateProgress(1);
9800         //    Roo.MessageBox.hide();
9801        // }
9802         //
9803         if(success){
9804             if(o.reset){
9805                 this.reset();
9806             }
9807             Roo.callback(o.success, o.scope, [this, action]);
9808             this.fireEvent('actioncomplete', this, action);
9809
9810         }else{
9811
9812             // failure condition..
9813             // we have a scenario where updates need confirming.
9814             // eg. if a locking scenario exists..
9815             // we look for { errors : { needs_confirm : true }} in the response.
9816             if (
9817                 (typeof(action.result) != 'undefined')  &&
9818                 (typeof(action.result.errors) != 'undefined')  &&
9819                 (typeof(action.result.errors.needs_confirm) != 'undefined')
9820            ){
9821                 var _t = this;
9822                 Roo.log("not supported yet");
9823                  /*
9824
9825                 Roo.MessageBox.confirm(
9826                     "Change requires confirmation",
9827                     action.result.errorMsg,
9828                     function(r) {
9829                         if (r != 'yes') {
9830                             return;
9831                         }
9832                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
9833                     }
9834
9835                 );
9836                 */
9837
9838
9839                 return;
9840             }
9841
9842             Roo.callback(o.failure, o.scope, [this, action]);
9843             // show an error message if no failed handler is set..
9844             if (!this.hasListener('actionfailed')) {
9845                 Roo.log("need to add dialog support");
9846                 /*
9847                 Roo.MessageBox.alert("Error",
9848                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9849                         action.result.errorMsg :
9850                         "Saving Failed, please check your entries or try again"
9851                 );
9852                 */
9853             }
9854
9855             this.fireEvent('actionfailed', this, action);
9856         }
9857
9858     },
9859     /**
9860      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9861      * @param {String} id The value to search for
9862      * @return Field
9863      */
9864     findField : function(id){
9865         var items = this.getItems();
9866         var field = items.get(id);
9867         if(!field){
9868              items.each(function(f){
9869                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9870                     field = f;
9871                     return false;
9872                 }
9873                 return true;
9874             });
9875         }
9876         return field || null;
9877     },
9878      /**
9879      * Mark fields in this form invalid in bulk.
9880      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9881      * @return {BasicForm} this
9882      */
9883     markInvalid : function(errors){
9884         if(errors instanceof Array){
9885             for(var i = 0, len = errors.length; i < len; i++){
9886                 var fieldError = errors[i];
9887                 var f = this.findField(fieldError.id);
9888                 if(f){
9889                     f.markInvalid(fieldError.msg);
9890                 }
9891             }
9892         }else{
9893             var field, id;
9894             for(id in errors){
9895                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9896                     field.markInvalid(errors[id]);
9897                 }
9898             }
9899         }
9900         //Roo.each(this.childForms || [], function (f) {
9901         //    f.markInvalid(errors);
9902         //});
9903
9904         return this;
9905     },
9906
9907     /**
9908      * Set values for fields in this form in bulk.
9909      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9910      * @return {BasicForm} this
9911      */
9912     setValues : function(values){
9913         if(values instanceof Array){ // array of objects
9914             for(var i = 0, len = values.length; i < len; i++){
9915                 var v = values[i];
9916                 var f = this.findField(v.id);
9917                 if(f){
9918                     f.setValue(v.value);
9919                     if(this.trackResetOnLoad){
9920                         f.originalValue = f.getValue();
9921                     }
9922                 }
9923             }
9924         }else{ // object hash
9925             var field, id;
9926             for(id in values){
9927                 if(typeof values[id] != 'function' && (field = this.findField(id))){
9928
9929                     if (field.setFromData &&
9930                         field.valueField &&
9931                         field.displayField &&
9932                         // combos' with local stores can
9933                         // be queried via setValue()
9934                         // to set their value..
9935                         (field.store && !field.store.isLocal)
9936                         ) {
9937                         // it's a combo
9938                         var sd = { };
9939                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9940                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9941                         field.setFromData(sd);
9942
9943                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9944                         
9945                         field.setFromData(values);
9946                         
9947                     } else {
9948                         field.setValue(values[id]);
9949                     }
9950
9951
9952                     if(this.trackResetOnLoad){
9953                         field.originalValue = field.getValue();
9954                     }
9955                 }
9956             }
9957         }
9958
9959         //Roo.each(this.childForms || [], function (f) {
9960         //    f.setValues(values);
9961         //});
9962
9963         return this;
9964     },
9965
9966     /**
9967      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9968      * they are returned as an array.
9969      * @param {Boolean} asString
9970      * @return {Object}
9971      */
9972     getValues : function(asString){
9973         //if (this.childForms) {
9974             // copy values from the child forms
9975         //    Roo.each(this.childForms, function (f) {
9976         //        this.setValues(f.getValues());
9977         //    }, this);
9978         //}
9979
9980
9981
9982         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9983         if(asString === true){
9984             return fs;
9985         }
9986         return Roo.urlDecode(fs);
9987     },
9988
9989     /**
9990      * Returns the fields in this form as an object with key/value pairs.
9991      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9992      * @return {Object}
9993      */
9994     getFieldValues : function(with_hidden)
9995     {
9996         var items = this.getItems();
9997         var ret = {};
9998         items.each(function(f){
9999             
10000             if (!f.getName()) {
10001                 return;
10002             }
10003             
10004             var v = f.getValue();
10005             
10006             if (f.inputType =='radio') {
10007                 if (typeof(ret[f.getName()]) == 'undefined') {
10008                     ret[f.getName()] = ''; // empty..
10009                 }
10010
10011                 if (!f.el.dom.checked) {
10012                     return;
10013
10014                 }
10015                 v = f.el.dom.value;
10016
10017             }
10018             
10019             if(f.xtype == 'MoneyField'){
10020                 ret[f.currencyName] = f.getCurrency();
10021             }
10022
10023             // not sure if this supported any more..
10024             if ((typeof(v) == 'object') && f.getRawValue) {
10025                 v = f.getRawValue() ; // dates..
10026             }
10027             // combo boxes where name != hiddenName...
10028             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10029                 ret[f.name] = f.getRawValue();
10030             }
10031             ret[f.getName()] = v;
10032         });
10033
10034         return ret;
10035     },
10036
10037     /**
10038      * Clears all invalid messages in this form.
10039      * @return {BasicForm} this
10040      */
10041     clearInvalid : function(){
10042         var items = this.getItems();
10043
10044         items.each(function(f){
10045            f.clearInvalid();
10046         });
10047
10048         return this;
10049     },
10050
10051     /**
10052      * Resets this form.
10053      * @return {BasicForm} this
10054      */
10055     reset : function(){
10056         var items = this.getItems();
10057         items.each(function(f){
10058             f.reset();
10059         });
10060
10061         Roo.each(this.childForms || [], function (f) {
10062             f.reset();
10063         });
10064
10065
10066         return this;
10067     },
10068     
10069     getItems : function()
10070     {
10071         var r=new Roo.util.MixedCollection(false, function(o){
10072             return o.id || (o.id = Roo.id());
10073         });
10074         var iter = function(el) {
10075             if (el.inputEl) {
10076                 r.add(el);
10077             }
10078             if (!el.items) {
10079                 return;
10080             }
10081             Roo.each(el.items,function(e) {
10082                 iter(e);
10083             });
10084         };
10085
10086         iter(this);
10087         return r;
10088     },
10089     
10090     hideFields : function(items)
10091     {
10092         Roo.each(items, function(i){
10093             
10094             var f = this.findField(i);
10095             
10096             if(!f){
10097                 return;
10098             }
10099             
10100             f.hide();
10101             
10102         }, this);
10103     },
10104     
10105     showFields : function(items)
10106     {
10107         Roo.each(items, function(i){
10108             
10109             var f = this.findField(i);
10110             
10111             if(!f){
10112                 return;
10113             }
10114             
10115             f.show();
10116             
10117         }, this);
10118     }
10119
10120 });
10121
10122 Roo.apply(Roo.bootstrap.Form, {
10123     
10124     popover : {
10125         
10126         padding : 5,
10127         
10128         isApplied : false,
10129         
10130         isMasked : false,
10131         
10132         form : false,
10133         
10134         target : false,
10135         
10136         toolTip : false,
10137         
10138         intervalID : false,
10139         
10140         maskEl : false,
10141         
10142         apply : function()
10143         {
10144             if(this.isApplied){
10145                 return;
10146             }
10147             
10148             this.maskEl = {
10149                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10150                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10151                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10152                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10153             };
10154             
10155             this.maskEl.top.enableDisplayMode("block");
10156             this.maskEl.left.enableDisplayMode("block");
10157             this.maskEl.bottom.enableDisplayMode("block");
10158             this.maskEl.right.enableDisplayMode("block");
10159             
10160             this.toolTip = new Roo.bootstrap.Tooltip({
10161                 cls : 'roo-form-error-popover',
10162                 alignment : {
10163                     'left' : ['r-l', [-2,0], 'right'],
10164                     'right' : ['l-r', [2,0], 'left'],
10165                     'bottom' : ['tl-bl', [0,2], 'top'],
10166                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10167                 }
10168             });
10169             
10170             this.toolTip.render(Roo.get(document.body));
10171
10172             this.toolTip.el.enableDisplayMode("block");
10173             
10174             Roo.get(document.body).on('click', function(){
10175                 this.unmask();
10176             }, this);
10177             
10178             Roo.get(document.body).on('touchstart', function(){
10179                 this.unmask();
10180             }, this);
10181             
10182             this.isApplied = true
10183         },
10184         
10185         mask : function(form, target)
10186         {
10187             this.form = form;
10188             
10189             this.target = target;
10190             
10191             if(!this.form.errorMask || !target.el){
10192                 return;
10193             }
10194             
10195             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10196             
10197             Roo.log(scrollable);
10198             
10199             var ot = this.target.el.calcOffsetsTo(scrollable);
10200             
10201             var scrollTo = ot[1] - this.form.maskOffset;
10202             
10203             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10204             
10205             scrollable.scrollTo('top', scrollTo);
10206             
10207             var box = this.target.el.getBox();
10208             Roo.log(box);
10209             var zIndex = Roo.bootstrap.Modal.zIndex++;
10210
10211             
10212             this.maskEl.top.setStyle('position', 'absolute');
10213             this.maskEl.top.setStyle('z-index', zIndex);
10214             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10215             this.maskEl.top.setLeft(0);
10216             this.maskEl.top.setTop(0);
10217             this.maskEl.top.show();
10218             
10219             this.maskEl.left.setStyle('position', 'absolute');
10220             this.maskEl.left.setStyle('z-index', zIndex);
10221             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10222             this.maskEl.left.setLeft(0);
10223             this.maskEl.left.setTop(box.y - this.padding);
10224             this.maskEl.left.show();
10225
10226             this.maskEl.bottom.setStyle('position', 'absolute');
10227             this.maskEl.bottom.setStyle('z-index', zIndex);
10228             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10229             this.maskEl.bottom.setLeft(0);
10230             this.maskEl.bottom.setTop(box.bottom + this.padding);
10231             this.maskEl.bottom.show();
10232
10233             this.maskEl.right.setStyle('position', 'absolute');
10234             this.maskEl.right.setStyle('z-index', zIndex);
10235             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10236             this.maskEl.right.setLeft(box.right + this.padding);
10237             this.maskEl.right.setTop(box.y - this.padding);
10238             this.maskEl.right.show();
10239
10240             this.toolTip.bindEl = this.target.el;
10241
10242             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10243
10244             var tip = this.target.blankText;
10245
10246             if(this.target.getValue() !== '' ) {
10247                 
10248                 if (this.target.invalidText.length) {
10249                     tip = this.target.invalidText;
10250                 } else if (this.target.regexText.length){
10251                     tip = this.target.regexText;
10252                 }
10253             }
10254
10255             this.toolTip.show(tip);
10256
10257             this.intervalID = window.setInterval(function() {
10258                 Roo.bootstrap.Form.popover.unmask();
10259             }, 10000);
10260
10261             window.onwheel = function(){ return false;};
10262             
10263             (function(){ this.isMasked = true; }).defer(500, this);
10264             
10265         },
10266         
10267         unmask : function()
10268         {
10269             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10270                 return;
10271             }
10272             
10273             this.maskEl.top.setStyle('position', 'absolute');
10274             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10275             this.maskEl.top.hide();
10276
10277             this.maskEl.left.setStyle('position', 'absolute');
10278             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10279             this.maskEl.left.hide();
10280
10281             this.maskEl.bottom.setStyle('position', 'absolute');
10282             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10283             this.maskEl.bottom.hide();
10284
10285             this.maskEl.right.setStyle('position', 'absolute');
10286             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10287             this.maskEl.right.hide();
10288             
10289             this.toolTip.hide();
10290             
10291             this.toolTip.el.hide();
10292             
10293             window.onwheel = function(){ return true;};
10294             
10295             if(this.intervalID){
10296                 window.clearInterval(this.intervalID);
10297                 this.intervalID = false;
10298             }
10299             
10300             this.isMasked = false;
10301             
10302         }
10303         
10304     }
10305     
10306 });
10307
10308 /*
10309  * Based on:
10310  * Ext JS Library 1.1.1
10311  * Copyright(c) 2006-2007, Ext JS, LLC.
10312  *
10313  * Originally Released Under LGPL - original licence link has changed is not relivant.
10314  *
10315  * Fork - LGPL
10316  * <script type="text/javascript">
10317  */
10318 /**
10319  * @class Roo.form.VTypes
10320  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10321  * @singleton
10322  */
10323 Roo.form.VTypes = function(){
10324     // closure these in so they are only created once.
10325     var alpha = /^[a-zA-Z_]+$/;
10326     var alphanum = /^[a-zA-Z0-9_]+$/;
10327     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10328     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10329
10330     // All these messages and functions are configurable
10331     return {
10332         /**
10333          * The function used to validate email addresses
10334          * @param {String} value The email address
10335          */
10336         'email' : function(v){
10337             return email.test(v);
10338         },
10339         /**
10340          * The error text to display when the email validation function returns false
10341          * @type String
10342          */
10343         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10344         /**
10345          * The keystroke filter mask to be applied on email input
10346          * @type RegExp
10347          */
10348         'emailMask' : /[a-z0-9_\.\-@]/i,
10349
10350         /**
10351          * The function used to validate URLs
10352          * @param {String} value The URL
10353          */
10354         'url' : function(v){
10355             return url.test(v);
10356         },
10357         /**
10358          * The error text to display when the url validation function returns false
10359          * @type String
10360          */
10361         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10362         
10363         /**
10364          * The function used to validate alpha values
10365          * @param {String} value The value
10366          */
10367         'alpha' : function(v){
10368             return alpha.test(v);
10369         },
10370         /**
10371          * The error text to display when the alpha validation function returns false
10372          * @type String
10373          */
10374         'alphaText' : 'This field should only contain letters and _',
10375         /**
10376          * The keystroke filter mask to be applied on alpha input
10377          * @type RegExp
10378          */
10379         'alphaMask' : /[a-z_]/i,
10380
10381         /**
10382          * The function used to validate alphanumeric values
10383          * @param {String} value The value
10384          */
10385         'alphanum' : function(v){
10386             return alphanum.test(v);
10387         },
10388         /**
10389          * The error text to display when the alphanumeric validation function returns false
10390          * @type String
10391          */
10392         'alphanumText' : 'This field should only contain letters, numbers and _',
10393         /**
10394          * The keystroke filter mask to be applied on alphanumeric input
10395          * @type RegExp
10396          */
10397         'alphanumMask' : /[a-z0-9_]/i
10398     };
10399 }();/*
10400  * - LGPL
10401  *
10402  * Input
10403  * 
10404  */
10405
10406 /**
10407  * @class Roo.bootstrap.Input
10408  * @extends Roo.bootstrap.Component
10409  * Bootstrap Input class
10410  * @cfg {Boolean} disabled is it disabled
10411  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10412  * @cfg {String} name name of the input
10413  * @cfg {string} fieldLabel - the label associated
10414  * @cfg {string} placeholder - placeholder to put in text.
10415  * @cfg {string}  before - input group add on before
10416  * @cfg {string} after - input group add on after
10417  * @cfg {string} size - (lg|sm) or leave empty..
10418  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10419  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10420  * @cfg {Number} md colspan out of 12 for computer-sized screens
10421  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10422  * @cfg {string} value default value of the input
10423  * @cfg {Number} labelWidth set the width of label 
10424  * @cfg {Number} labellg set the width of label (1-12)
10425  * @cfg {Number} labelmd set the width of label (1-12)
10426  * @cfg {Number} labelsm set the width of label (1-12)
10427  * @cfg {Number} labelxs set the width of label (1-12)
10428  * @cfg {String} labelAlign (top|left)
10429  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10430  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10431  * @cfg {String} indicatorpos (left|right) default left
10432  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10433  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10434  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10435
10436  * @cfg {String} align (left|center|right) Default left
10437  * @cfg {Boolean} forceFeedback (true|false) Default false
10438  * 
10439  * @constructor
10440  * Create a new Input
10441  * @param {Object} config The config object
10442  */
10443
10444 Roo.bootstrap.Input = function(config){
10445     
10446     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10447     
10448     this.addEvents({
10449         /**
10450          * @event focus
10451          * Fires when this field receives input focus.
10452          * @param {Roo.form.Field} this
10453          */
10454         focus : true,
10455         /**
10456          * @event blur
10457          * Fires when this field loses input focus.
10458          * @param {Roo.form.Field} this
10459          */
10460         blur : true,
10461         /**
10462          * @event specialkey
10463          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10464          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10465          * @param {Roo.form.Field} this
10466          * @param {Roo.EventObject} e The event object
10467          */
10468         specialkey : true,
10469         /**
10470          * @event change
10471          * Fires just before the field blurs if the field value has changed.
10472          * @param {Roo.form.Field} this
10473          * @param {Mixed} newValue The new value
10474          * @param {Mixed} oldValue The original value
10475          */
10476         change : true,
10477         /**
10478          * @event invalid
10479          * Fires after the field has been marked as invalid.
10480          * @param {Roo.form.Field} this
10481          * @param {String} msg The validation message
10482          */
10483         invalid : true,
10484         /**
10485          * @event valid
10486          * Fires after the field has been validated with no errors.
10487          * @param {Roo.form.Field} this
10488          */
10489         valid : true,
10490          /**
10491          * @event keyup
10492          * Fires after the key up
10493          * @param {Roo.form.Field} this
10494          * @param {Roo.EventObject}  e The event Object
10495          */
10496         keyup : true
10497     });
10498 };
10499
10500 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10501      /**
10502      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10503       automatic validation (defaults to "keyup").
10504      */
10505     validationEvent : "keyup",
10506      /**
10507      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10508      */
10509     validateOnBlur : true,
10510     /**
10511      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10512      */
10513     validationDelay : 250,
10514      /**
10515      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10516      */
10517     focusClass : "x-form-focus",  // not needed???
10518     
10519        
10520     /**
10521      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10522      */
10523     invalidClass : "has-warning",
10524     
10525     /**
10526      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10527      */
10528     validClass : "has-success",
10529     
10530     /**
10531      * @cfg {Boolean} hasFeedback (true|false) default true
10532      */
10533     hasFeedback : true,
10534     
10535     /**
10536      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10537      */
10538     invalidFeedbackClass : "glyphicon-warning-sign",
10539     
10540     /**
10541      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10542      */
10543     validFeedbackClass : "glyphicon-ok",
10544     
10545     /**
10546      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10547      */
10548     selectOnFocus : false,
10549     
10550      /**
10551      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10552      */
10553     maskRe : null,
10554        /**
10555      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10556      */
10557     vtype : null,
10558     
10559       /**
10560      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10561      */
10562     disableKeyFilter : false,
10563     
10564        /**
10565      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10566      */
10567     disabled : false,
10568      /**
10569      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10570      */
10571     allowBlank : true,
10572     /**
10573      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10574      */
10575     blankText : "Please complete this mandatory field",
10576     
10577      /**
10578      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10579      */
10580     minLength : 0,
10581     /**
10582      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10583      */
10584     maxLength : Number.MAX_VALUE,
10585     /**
10586      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10587      */
10588     minLengthText : "The minimum length for this field is {0}",
10589     /**
10590      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10591      */
10592     maxLengthText : "The maximum length for this field is {0}",
10593   
10594     
10595     /**
10596      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10597      * If available, this function will be called only after the basic validators all return true, and will be passed the
10598      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10599      */
10600     validator : null,
10601     /**
10602      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10603      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10604      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10605      */
10606     regex : null,
10607     /**
10608      * @cfg {String} regexText -- Depricated - use Invalid Text
10609      */
10610     regexText : "",
10611     
10612     /**
10613      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10614      */
10615     invalidText : "",
10616     
10617     
10618     
10619     autocomplete: false,
10620     
10621     
10622     fieldLabel : '',
10623     inputType : 'text',
10624     
10625     name : false,
10626     placeholder: false,
10627     before : false,
10628     after : false,
10629     size : false,
10630     hasFocus : false,
10631     preventMark: false,
10632     isFormField : true,
10633     value : '',
10634     labelWidth : 2,
10635     labelAlign : false,
10636     readOnly : false,
10637     align : false,
10638     formatedValue : false,
10639     forceFeedback : false,
10640     
10641     indicatorpos : 'left',
10642     
10643     labellg : 0,
10644     labelmd : 0,
10645     labelsm : 0,
10646     labelxs : 0,
10647     
10648     capture : '',
10649     accept : '',
10650     
10651     parentLabelAlign : function()
10652     {
10653         var parent = this;
10654         while (parent.parent()) {
10655             parent = parent.parent();
10656             if (typeof(parent.labelAlign) !='undefined') {
10657                 return parent.labelAlign;
10658             }
10659         }
10660         return 'left';
10661         
10662     },
10663     
10664     getAutoCreate : function()
10665     {
10666         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10667         
10668         var id = Roo.id();
10669         
10670         var cfg = {};
10671         
10672         if(this.inputType != 'hidden'){
10673             cfg.cls = 'form-group' //input-group
10674         }
10675         
10676         var input =  {
10677             tag: 'input',
10678             id : id,
10679             type : this.inputType,
10680             value : this.value,
10681             cls : 'form-control',
10682             placeholder : this.placeholder || '',
10683             autocomplete : this.autocomplete || 'new-password'
10684         };
10685         if (this.inputType == 'file') {
10686             input.style = 'overflow:hidden'; // why not in CSS?
10687         }
10688         
10689         if(this.capture.length){
10690             input.capture = this.capture;
10691         }
10692         
10693         if(this.accept.length){
10694             input.accept = this.accept + "/*";
10695         }
10696         
10697         if(this.align){
10698             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10699         }
10700         
10701         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10702             input.maxLength = this.maxLength;
10703         }
10704         
10705         if (this.disabled) {
10706             input.disabled=true;
10707         }
10708         
10709         if (this.readOnly) {
10710             input.readonly=true;
10711         }
10712         
10713         if (this.name) {
10714             input.name = this.name;
10715         }
10716         
10717         if (this.size) {
10718             input.cls += ' input-' + this.size;
10719         }
10720         
10721         var settings=this;
10722         ['xs','sm','md','lg'].map(function(size){
10723             if (settings[size]) {
10724                 cfg.cls += ' col-' + size + '-' + settings[size];
10725             }
10726         });
10727         
10728         var inputblock = input;
10729         
10730         var feedback = {
10731             tag: 'span',
10732             cls: 'glyphicon form-control-feedback'
10733         };
10734             
10735         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10736             
10737             inputblock = {
10738                 cls : 'has-feedback',
10739                 cn :  [
10740                     input,
10741                     feedback
10742                 ] 
10743             };  
10744         }
10745         
10746         if (this.before || this.after) {
10747             
10748             inputblock = {
10749                 cls : 'input-group',
10750                 cn :  [] 
10751             };
10752             
10753             if (this.before && typeof(this.before) == 'string') {
10754                 
10755                 inputblock.cn.push({
10756                     tag :'span',
10757                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10758                     html : this.before
10759                 });
10760             }
10761             if (this.before && typeof(this.before) == 'object') {
10762                 this.before = Roo.factory(this.before);
10763                 
10764                 inputblock.cn.push({
10765                     tag :'span',
10766                     cls : 'roo-input-before input-group-prepend   input-group-' +
10767                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10768                 });
10769             }
10770             
10771             inputblock.cn.push(input);
10772             
10773             if (this.after && typeof(this.after) == 'string') {
10774                 inputblock.cn.push({
10775                     tag :'span',
10776                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10777                     html : this.after
10778                 });
10779             }
10780             if (this.after && typeof(this.after) == 'object') {
10781                 this.after = Roo.factory(this.after);
10782                 
10783                 inputblock.cn.push({
10784                     tag :'span',
10785                     cls : 'roo-input-after input-group-append  input-group-' +
10786                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10787                 });
10788             }
10789             
10790             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10791                 inputblock.cls += ' has-feedback';
10792                 inputblock.cn.push(feedback);
10793             }
10794         };
10795         var indicator = {
10796             tag : 'i',
10797             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10798             tooltip : 'This field is required'
10799         };
10800         if (this.allowBlank ) {
10801             indicator.style = this.allowBlank ? ' display:none' : '';
10802         }
10803         if (align ==='left' && this.fieldLabel.length) {
10804             
10805             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10806             
10807             cfg.cn = [
10808                 indicator,
10809                 {
10810                     tag: 'label',
10811                     'for' :  id,
10812                     cls : 'control-label col-form-label',
10813                     html : this.fieldLabel
10814
10815                 },
10816                 {
10817                     cls : "", 
10818                     cn: [
10819                         inputblock
10820                     ]
10821                 }
10822             ];
10823             
10824             var labelCfg = cfg.cn[1];
10825             var contentCfg = cfg.cn[2];
10826             
10827             if(this.indicatorpos == 'right'){
10828                 cfg.cn = [
10829                     {
10830                         tag: 'label',
10831                         'for' :  id,
10832                         cls : 'control-label col-form-label',
10833                         cn : [
10834                             {
10835                                 tag : 'span',
10836                                 html : this.fieldLabel
10837                             },
10838                             indicator
10839                         ]
10840                     },
10841                     {
10842                         cls : "",
10843                         cn: [
10844                             inputblock
10845                         ]
10846                     }
10847
10848                 ];
10849                 
10850                 labelCfg = cfg.cn[0];
10851                 contentCfg = cfg.cn[1];
10852             
10853             }
10854             
10855             if(this.labelWidth > 12){
10856                 labelCfg.style = "width: " + this.labelWidth + 'px';
10857             }
10858             
10859             if(this.labelWidth < 13 && this.labelmd == 0){
10860                 this.labelmd = this.labelWidth;
10861             }
10862             
10863             if(this.labellg > 0){
10864                 labelCfg.cls += ' col-lg-' + this.labellg;
10865                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10866             }
10867             
10868             if(this.labelmd > 0){
10869                 labelCfg.cls += ' col-md-' + this.labelmd;
10870                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10871             }
10872             
10873             if(this.labelsm > 0){
10874                 labelCfg.cls += ' col-sm-' + this.labelsm;
10875                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10876             }
10877             
10878             if(this.labelxs > 0){
10879                 labelCfg.cls += ' col-xs-' + this.labelxs;
10880                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10881             }
10882             
10883             
10884         } else if ( this.fieldLabel.length) {
10885                 
10886             
10887             
10888             cfg.cn = [
10889                 {
10890                     tag : 'i',
10891                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10892                     tooltip : 'This field is required',
10893                     style : this.allowBlank ? ' display:none' : '' 
10894                 },
10895                 {
10896                     tag: 'label',
10897                    //cls : 'input-group-addon',
10898                     html : this.fieldLabel
10899
10900                 },
10901
10902                inputblock
10903
10904            ];
10905            
10906            if(this.indicatorpos == 'right'){
10907        
10908                 cfg.cn = [
10909                     {
10910                         tag: 'label',
10911                        //cls : 'input-group-addon',
10912                         html : this.fieldLabel
10913
10914                     },
10915                     {
10916                         tag : 'i',
10917                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10918                         tooltip : 'This field is required',
10919                         style : this.allowBlank ? ' display:none' : '' 
10920                     },
10921
10922                    inputblock
10923
10924                ];
10925
10926             }
10927
10928         } else {
10929             
10930             cfg.cn = [
10931
10932                     inputblock
10933
10934             ];
10935                 
10936                 
10937         };
10938         
10939         if (this.parentType === 'Navbar' &&  this.parent().bar) {
10940            cfg.cls += ' navbar-form';
10941         }
10942         
10943         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10944             // on BS4 we do this only if not form 
10945             cfg.cls += ' navbar-form';
10946             cfg.tag = 'li';
10947         }
10948         
10949         return cfg;
10950         
10951     },
10952     /**
10953      * return the real input element.
10954      */
10955     inputEl: function ()
10956     {
10957         return this.el.select('input.form-control',true).first();
10958     },
10959     
10960     tooltipEl : function()
10961     {
10962         return this.inputEl();
10963     },
10964     
10965     indicatorEl : function()
10966     {
10967         if (Roo.bootstrap.version == 4) {
10968             return false; // not enabled in v4 yet.
10969         }
10970         
10971         var indicator = this.el.select('i.roo-required-indicator',true).first();
10972         
10973         if(!indicator){
10974             return false;
10975         }
10976         
10977         return indicator;
10978         
10979     },
10980     
10981     setDisabled : function(v)
10982     {
10983         var i  = this.inputEl().dom;
10984         if (!v) {
10985             i.removeAttribute('disabled');
10986             return;
10987             
10988         }
10989         i.setAttribute('disabled','true');
10990     },
10991     initEvents : function()
10992     {
10993           
10994         this.inputEl().on("keydown" , this.fireKey,  this);
10995         this.inputEl().on("focus", this.onFocus,  this);
10996         this.inputEl().on("blur", this.onBlur,  this);
10997         
10998         this.inputEl().relayEvent('keyup', this);
10999         
11000         this.indicator = this.indicatorEl();
11001         
11002         if(this.indicator){
11003             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
11004         }
11005  
11006         // reference to original value for reset
11007         this.originalValue = this.getValue();
11008         //Roo.form.TextField.superclass.initEvents.call(this);
11009         if(this.validationEvent == 'keyup'){
11010             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11011             this.inputEl().on('keyup', this.filterValidation, this);
11012         }
11013         else if(this.validationEvent !== false){
11014             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11015         }
11016         
11017         if(this.selectOnFocus){
11018             this.on("focus", this.preFocus, this);
11019             
11020         }
11021         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11022             this.inputEl().on("keypress", this.filterKeys, this);
11023         } else {
11024             this.inputEl().relayEvent('keypress', this);
11025         }
11026        /* if(this.grow){
11027             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11028             this.el.on("click", this.autoSize,  this);
11029         }
11030         */
11031         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11032             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11033         }
11034         
11035         if (typeof(this.before) == 'object') {
11036             this.before.render(this.el.select('.roo-input-before',true).first());
11037         }
11038         if (typeof(this.after) == 'object') {
11039             this.after.render(this.el.select('.roo-input-after',true).first());
11040         }
11041         
11042         this.inputEl().on('change', this.onChange, this);
11043         
11044     },
11045     filterValidation : function(e){
11046         if(!e.isNavKeyPress()){
11047             this.validationTask.delay(this.validationDelay);
11048         }
11049     },
11050      /**
11051      * Validates the field value
11052      * @return {Boolean} True if the value is valid, else false
11053      */
11054     validate : function(){
11055         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11056         if(this.disabled || this.validateValue(this.getRawValue())){
11057             this.markValid();
11058             return true;
11059         }
11060         
11061         this.markInvalid();
11062         return false;
11063     },
11064     
11065     
11066     /**
11067      * Validates a value according to the field's validation rules and marks the field as invalid
11068      * if the validation fails
11069      * @param {Mixed} value The value to validate
11070      * @return {Boolean} True if the value is valid, else false
11071      */
11072     validateValue : function(value)
11073     {
11074         if(this.getVisibilityEl().hasClass('hidden')){
11075             return true;
11076         }
11077         
11078         if(value.length < 1)  { // if it's blank
11079             if(this.allowBlank){
11080                 return true;
11081             }
11082             return false;
11083         }
11084         
11085         if(value.length < this.minLength){
11086             return false;
11087         }
11088         if(value.length > this.maxLength){
11089             return false;
11090         }
11091         if(this.vtype){
11092             var vt = Roo.form.VTypes;
11093             if(!vt[this.vtype](value, this)){
11094                 return false;
11095             }
11096         }
11097         if(typeof this.validator == "function"){
11098             var msg = this.validator(value);
11099             if(msg !== true){
11100                 return false;
11101             }
11102             if (typeof(msg) == 'string') {
11103                 this.invalidText = msg;
11104             }
11105         }
11106         
11107         if(this.regex && !this.regex.test(value)){
11108             return false;
11109         }
11110         
11111         return true;
11112     },
11113     
11114      // private
11115     fireKey : function(e){
11116         //Roo.log('field ' + e.getKey());
11117         if(e.isNavKeyPress()){
11118             this.fireEvent("specialkey", this, e);
11119         }
11120     },
11121     focus : function (selectText){
11122         if(this.rendered){
11123             this.inputEl().focus();
11124             if(selectText === true){
11125                 this.inputEl().dom.select();
11126             }
11127         }
11128         return this;
11129     } ,
11130     
11131     onFocus : function(){
11132         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11133            // this.el.addClass(this.focusClass);
11134         }
11135         if(!this.hasFocus){
11136             this.hasFocus = true;
11137             this.startValue = this.getValue();
11138             this.fireEvent("focus", this);
11139         }
11140     },
11141     
11142     beforeBlur : Roo.emptyFn,
11143
11144     
11145     // private
11146     onBlur : function(){
11147         this.beforeBlur();
11148         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11149             //this.el.removeClass(this.focusClass);
11150         }
11151         this.hasFocus = false;
11152         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11153             this.validate();
11154         }
11155         var v = this.getValue();
11156         if(String(v) !== String(this.startValue)){
11157             this.fireEvent('change', this, v, this.startValue);
11158         }
11159         this.fireEvent("blur", this);
11160     },
11161     
11162     onChange : function(e)
11163     {
11164         var v = this.getValue();
11165         if(String(v) !== String(this.startValue)){
11166             this.fireEvent('change', this, v, this.startValue);
11167         }
11168         
11169     },
11170     
11171     /**
11172      * Resets the current field value to the originally loaded value and clears any validation messages
11173      */
11174     reset : function(){
11175         this.setValue(this.originalValue);
11176         this.validate();
11177     },
11178      /**
11179      * Returns the name of the field
11180      * @return {Mixed} name The name field
11181      */
11182     getName: function(){
11183         return this.name;
11184     },
11185      /**
11186      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11187      * @return {Mixed} value The field value
11188      */
11189     getValue : function(){
11190         
11191         var v = this.inputEl().getValue();
11192         
11193         return v;
11194     },
11195     /**
11196      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11197      * @return {Mixed} value The field value
11198      */
11199     getRawValue : function(){
11200         var v = this.inputEl().getValue();
11201         
11202         return v;
11203     },
11204     
11205     /**
11206      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11207      * @param {Mixed} value The value to set
11208      */
11209     setRawValue : function(v){
11210         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11211     },
11212     
11213     selectText : function(start, end){
11214         var v = this.getRawValue();
11215         if(v.length > 0){
11216             start = start === undefined ? 0 : start;
11217             end = end === undefined ? v.length : end;
11218             var d = this.inputEl().dom;
11219             if(d.setSelectionRange){
11220                 d.setSelectionRange(start, end);
11221             }else if(d.createTextRange){
11222                 var range = d.createTextRange();
11223                 range.moveStart("character", start);
11224                 range.moveEnd("character", v.length-end);
11225                 range.select();
11226             }
11227         }
11228     },
11229     
11230     /**
11231      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11232      * @param {Mixed} value The value to set
11233      */
11234     setValue : function(v){
11235         this.value = v;
11236         if(this.rendered){
11237             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11238             this.validate();
11239         }
11240     },
11241     
11242     /*
11243     processValue : function(value){
11244         if(this.stripCharsRe){
11245             var newValue = value.replace(this.stripCharsRe, '');
11246             if(newValue !== value){
11247                 this.setRawValue(newValue);
11248                 return newValue;
11249             }
11250         }
11251         return value;
11252     },
11253   */
11254     preFocus : function(){
11255         
11256         if(this.selectOnFocus){
11257             this.inputEl().dom.select();
11258         }
11259     },
11260     filterKeys : function(e){
11261         var k = e.getKey();
11262         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11263             return;
11264         }
11265         var c = e.getCharCode(), cc = String.fromCharCode(c);
11266         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11267             return;
11268         }
11269         if(!this.maskRe.test(cc)){
11270             e.stopEvent();
11271         }
11272     },
11273      /**
11274      * Clear any invalid styles/messages for this field
11275      */
11276     clearInvalid : function(){
11277         
11278         if(!this.el || this.preventMark){ // not rendered
11279             return;
11280         }
11281         
11282         
11283         this.el.removeClass([this.invalidClass, 'is-invalid']);
11284         
11285         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11286             
11287             var feedback = this.el.select('.form-control-feedback', true).first();
11288             
11289             if(feedback){
11290                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11291             }
11292             
11293         }
11294         
11295         if(this.indicator){
11296             this.indicator.removeClass('visible');
11297             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11298         }
11299         
11300         this.fireEvent('valid', this);
11301     },
11302     
11303      /**
11304      * Mark this field as valid
11305      */
11306     markValid : function()
11307     {
11308         if(!this.el  || this.preventMark){ // not rendered...
11309             return;
11310         }
11311         
11312         this.el.removeClass([this.invalidClass, this.validClass]);
11313         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11314
11315         var feedback = this.el.select('.form-control-feedback', true).first();
11316             
11317         if(feedback){
11318             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11319         }
11320         
11321         if(this.indicator){
11322             this.indicator.removeClass('visible');
11323             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11324         }
11325         
11326         if(this.disabled){
11327             return;
11328         }
11329         
11330            
11331         if(this.allowBlank && !this.getRawValue().length){
11332             return;
11333         }
11334         if (Roo.bootstrap.version == 3) {
11335             this.el.addClass(this.validClass);
11336         } else {
11337             this.inputEl().addClass('is-valid');
11338         }
11339
11340         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11341             
11342             var feedback = this.el.select('.form-control-feedback', true).first();
11343             
11344             if(feedback){
11345                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11346                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11347             }
11348             
11349         }
11350         
11351         this.fireEvent('valid', this);
11352     },
11353     
11354      /**
11355      * Mark this field as invalid
11356      * @param {String} msg The validation message
11357      */
11358     markInvalid : function(msg)
11359     {
11360         if(!this.el  || this.preventMark){ // not rendered
11361             return;
11362         }
11363         
11364         this.el.removeClass([this.invalidClass, this.validClass]);
11365         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11366         
11367         var feedback = this.el.select('.form-control-feedback', true).first();
11368             
11369         if(feedback){
11370             this.el.select('.form-control-feedback', true).first().removeClass(
11371                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11372         }
11373
11374         if(this.disabled){
11375             return;
11376         }
11377         
11378         if(this.allowBlank && !this.getRawValue().length){
11379             return;
11380         }
11381         
11382         if(this.indicator){
11383             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11384             this.indicator.addClass('visible');
11385         }
11386         if (Roo.bootstrap.version == 3) {
11387             this.el.addClass(this.invalidClass);
11388         } else {
11389             this.inputEl().addClass('is-invalid');
11390         }
11391         
11392         
11393         
11394         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11395             
11396             var feedback = this.el.select('.form-control-feedback', true).first();
11397             
11398             if(feedback){
11399                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11400                 
11401                 if(this.getValue().length || this.forceFeedback){
11402                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11403                 }
11404                 
11405             }
11406             
11407         }
11408         
11409         this.fireEvent('invalid', this, msg);
11410     },
11411     // private
11412     SafariOnKeyDown : function(event)
11413     {
11414         // this is a workaround for a password hang bug on chrome/ webkit.
11415         if (this.inputEl().dom.type != 'password') {
11416             return;
11417         }
11418         
11419         var isSelectAll = false;
11420         
11421         if(this.inputEl().dom.selectionEnd > 0){
11422             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11423         }
11424         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11425             event.preventDefault();
11426             this.setValue('');
11427             return;
11428         }
11429         
11430         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11431             
11432             event.preventDefault();
11433             // this is very hacky as keydown always get's upper case.
11434             //
11435             var cc = String.fromCharCode(event.getCharCode());
11436             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11437             
11438         }
11439     },
11440     adjustWidth : function(tag, w){
11441         tag = tag.toLowerCase();
11442         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11443             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11444                 if(tag == 'input'){
11445                     return w + 2;
11446                 }
11447                 if(tag == 'textarea'){
11448                     return w-2;
11449                 }
11450             }else if(Roo.isOpera){
11451                 if(tag == 'input'){
11452                     return w + 2;
11453                 }
11454                 if(tag == 'textarea'){
11455                     return w-2;
11456                 }
11457             }
11458         }
11459         return w;
11460     },
11461     
11462     setFieldLabel : function(v)
11463     {
11464         if(!this.rendered){
11465             return;
11466         }
11467         
11468         if(this.indicatorEl()){
11469             var ar = this.el.select('label > span',true);
11470             
11471             if (ar.elements.length) {
11472                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11473                 this.fieldLabel = v;
11474                 return;
11475             }
11476             
11477             var br = this.el.select('label',true);
11478             
11479             if(br.elements.length) {
11480                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11481                 this.fieldLabel = v;
11482                 return;
11483             }
11484             
11485             Roo.log('Cannot Found any of label > span || label in input');
11486             return;
11487         }
11488         
11489         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11490         this.fieldLabel = v;
11491         
11492         
11493     }
11494 });
11495
11496  
11497 /*
11498  * - LGPL
11499  *
11500  * Input
11501  * 
11502  */
11503
11504 /**
11505  * @class Roo.bootstrap.TextArea
11506  * @extends Roo.bootstrap.Input
11507  * Bootstrap TextArea class
11508  * @cfg {Number} cols Specifies the visible width of a text area
11509  * @cfg {Number} rows Specifies the visible number of lines in a text area
11510  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11511  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11512  * @cfg {string} html text
11513  * 
11514  * @constructor
11515  * Create a new TextArea
11516  * @param {Object} config The config object
11517  */
11518
11519 Roo.bootstrap.TextArea = function(config){
11520     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11521    
11522 };
11523
11524 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11525      
11526     cols : false,
11527     rows : 5,
11528     readOnly : false,
11529     warp : 'soft',
11530     resize : false,
11531     value: false,
11532     html: false,
11533     
11534     getAutoCreate : function(){
11535         
11536         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11537         
11538         var id = Roo.id();
11539         
11540         var cfg = {};
11541         
11542         if(this.inputType != 'hidden'){
11543             cfg.cls = 'form-group' //input-group
11544         }
11545         
11546         var input =  {
11547             tag: 'textarea',
11548             id : id,
11549             warp : this.warp,
11550             rows : this.rows,
11551             value : this.value || '',
11552             html: this.html || '',
11553             cls : 'form-control',
11554             placeholder : this.placeholder || '' 
11555             
11556         };
11557         
11558         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11559             input.maxLength = this.maxLength;
11560         }
11561         
11562         if(this.resize){
11563             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11564         }
11565         
11566         if(this.cols){
11567             input.cols = this.cols;
11568         }
11569         
11570         if (this.readOnly) {
11571             input.readonly = true;
11572         }
11573         
11574         if (this.name) {
11575             input.name = this.name;
11576         }
11577         
11578         if (this.size) {
11579             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11580         }
11581         
11582         var settings=this;
11583         ['xs','sm','md','lg'].map(function(size){
11584             if (settings[size]) {
11585                 cfg.cls += ' col-' + size + '-' + settings[size];
11586             }
11587         });
11588         
11589         var inputblock = input;
11590         
11591         if(this.hasFeedback && !this.allowBlank){
11592             
11593             var feedback = {
11594                 tag: 'span',
11595                 cls: 'glyphicon form-control-feedback'
11596             };
11597
11598             inputblock = {
11599                 cls : 'has-feedback',
11600                 cn :  [
11601                     input,
11602                     feedback
11603                 ] 
11604             };  
11605         }
11606         
11607         
11608         if (this.before || this.after) {
11609             
11610             inputblock = {
11611                 cls : 'input-group',
11612                 cn :  [] 
11613             };
11614             if (this.before) {
11615                 inputblock.cn.push({
11616                     tag :'span',
11617                     cls : 'input-group-addon',
11618                     html : this.before
11619                 });
11620             }
11621             
11622             inputblock.cn.push(input);
11623             
11624             if(this.hasFeedback && !this.allowBlank){
11625                 inputblock.cls += ' has-feedback';
11626                 inputblock.cn.push(feedback);
11627             }
11628             
11629             if (this.after) {
11630                 inputblock.cn.push({
11631                     tag :'span',
11632                     cls : 'input-group-addon',
11633                     html : this.after
11634                 });
11635             }
11636             
11637         }
11638         
11639         if (align ==='left' && this.fieldLabel.length) {
11640             cfg.cn = [
11641                 {
11642                     tag: 'label',
11643                     'for' :  id,
11644                     cls : 'control-label',
11645                     html : this.fieldLabel
11646                 },
11647                 {
11648                     cls : "",
11649                     cn: [
11650                         inputblock
11651                     ]
11652                 }
11653
11654             ];
11655             
11656             if(this.labelWidth > 12){
11657                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11658             }
11659
11660             if(this.labelWidth < 13 && this.labelmd == 0){
11661                 this.labelmd = this.labelWidth;
11662             }
11663
11664             if(this.labellg > 0){
11665                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11666                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11667             }
11668
11669             if(this.labelmd > 0){
11670                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11671                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11672             }
11673
11674             if(this.labelsm > 0){
11675                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11676                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11677             }
11678
11679             if(this.labelxs > 0){
11680                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11681                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11682             }
11683             
11684         } else if ( this.fieldLabel.length) {
11685             cfg.cn = [
11686
11687                {
11688                    tag: 'label',
11689                    //cls : 'input-group-addon',
11690                    html : this.fieldLabel
11691
11692                },
11693
11694                inputblock
11695
11696            ];
11697
11698         } else {
11699
11700             cfg.cn = [
11701
11702                 inputblock
11703
11704             ];
11705                 
11706         }
11707         
11708         if (this.disabled) {
11709             input.disabled=true;
11710         }
11711         
11712         return cfg;
11713         
11714     },
11715     /**
11716      * return the real textarea element.
11717      */
11718     inputEl: function ()
11719     {
11720         return this.el.select('textarea.form-control',true).first();
11721     },
11722     
11723     /**
11724      * Clear any invalid styles/messages for this field
11725      */
11726     clearInvalid : function()
11727     {
11728         
11729         if(!this.el || this.preventMark){ // not rendered
11730             return;
11731         }
11732         
11733         var label = this.el.select('label', true).first();
11734         var icon = this.el.select('i.fa-star', true).first();
11735         
11736         if(label && icon){
11737             icon.remove();
11738         }
11739         this.el.removeClass( this.validClass);
11740         this.inputEl().removeClass('is-invalid');
11741          
11742         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11743             
11744             var feedback = this.el.select('.form-control-feedback', true).first();
11745             
11746             if(feedback){
11747                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11748             }
11749             
11750         }
11751         
11752         this.fireEvent('valid', this);
11753     },
11754     
11755      /**
11756      * Mark this field as valid
11757      */
11758     markValid : function()
11759     {
11760         if(!this.el  || this.preventMark){ // not rendered
11761             return;
11762         }
11763         
11764         this.el.removeClass([this.invalidClass, this.validClass]);
11765         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11766         
11767         var feedback = this.el.select('.form-control-feedback', true).first();
11768             
11769         if(feedback){
11770             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11771         }
11772
11773         if(this.disabled || this.allowBlank){
11774             return;
11775         }
11776         
11777         var label = this.el.select('label', true).first();
11778         var icon = this.el.select('i.fa-star', true).first();
11779         
11780         if(label && icon){
11781             icon.remove();
11782         }
11783         if (Roo.bootstrap.version == 3) {
11784             this.el.addClass(this.validClass);
11785         } else {
11786             this.inputEl().addClass('is-valid');
11787         }
11788         
11789         
11790         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11791             
11792             var feedback = this.el.select('.form-control-feedback', true).first();
11793             
11794             if(feedback){
11795                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11796                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11797             }
11798             
11799         }
11800         
11801         this.fireEvent('valid', this);
11802     },
11803     
11804      /**
11805      * Mark this field as invalid
11806      * @param {String} msg The validation message
11807      */
11808     markInvalid : function(msg)
11809     {
11810         if(!this.el  || this.preventMark){ // not rendered
11811             return;
11812         }
11813         
11814         this.el.removeClass([this.invalidClass, this.validClass]);
11815         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11816         
11817         var feedback = this.el.select('.form-control-feedback', true).first();
11818             
11819         if(feedback){
11820             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11821         }
11822
11823         if(this.disabled || this.allowBlank){
11824             return;
11825         }
11826         
11827         var label = this.el.select('label', true).first();
11828         var icon = this.el.select('i.fa-star', true).first();
11829         
11830         if(!this.getValue().length && label && !icon){
11831             this.el.createChild({
11832                 tag : 'i',
11833                 cls : 'text-danger fa fa-lg fa-star',
11834                 tooltip : 'This field is required',
11835                 style : 'margin-right:5px;'
11836             }, label, true);
11837         }
11838         
11839         if (Roo.bootstrap.version == 3) {
11840             this.el.addClass(this.invalidClass);
11841         } else {
11842             this.inputEl().addClass('is-invalid');
11843         }
11844         
11845         // fixme ... this may be depricated need to test..
11846         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11847             
11848             var feedback = this.el.select('.form-control-feedback', true).first();
11849             
11850             if(feedback){
11851                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11852                 
11853                 if(this.getValue().length || this.forceFeedback){
11854                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11855                 }
11856                 
11857             }
11858             
11859         }
11860         
11861         this.fireEvent('invalid', this, msg);
11862     }
11863 });
11864
11865  
11866 /*
11867  * - LGPL
11868  *
11869  * trigger field - base class for combo..
11870  * 
11871  */
11872  
11873 /**
11874  * @class Roo.bootstrap.TriggerField
11875  * @extends Roo.bootstrap.Input
11876  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11877  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11878  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11879  * for which you can provide a custom implementation.  For example:
11880  * <pre><code>
11881 var trigger = new Roo.bootstrap.TriggerField();
11882 trigger.onTriggerClick = myTriggerFn;
11883 trigger.applyTo('my-field');
11884 </code></pre>
11885  *
11886  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11887  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11888  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
11889  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11890  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11891
11892  * @constructor
11893  * Create a new TriggerField.
11894  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11895  * to the base TextField)
11896  */
11897 Roo.bootstrap.TriggerField = function(config){
11898     this.mimicing = false;
11899     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11900 };
11901
11902 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
11903     /**
11904      * @cfg {String} triggerClass A CSS class to apply to the trigger
11905      */
11906      /**
11907      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11908      */
11909     hideTrigger:false,
11910
11911     /**
11912      * @cfg {Boolean} removable (true|false) special filter default false
11913      */
11914     removable : false,
11915     
11916     /** @cfg {Boolean} grow @hide */
11917     /** @cfg {Number} growMin @hide */
11918     /** @cfg {Number} growMax @hide */
11919
11920     /**
11921      * @hide 
11922      * @method
11923      */
11924     autoSize: Roo.emptyFn,
11925     // private
11926     monitorTab : true,
11927     // private
11928     deferHeight : true,
11929
11930     
11931     actionMode : 'wrap',
11932     
11933     caret : false,
11934     
11935     
11936     getAutoCreate : function(){
11937        
11938         var align = this.labelAlign || this.parentLabelAlign();
11939         
11940         var id = Roo.id();
11941         
11942         var cfg = {
11943             cls: 'form-group' //input-group
11944         };
11945         
11946         
11947         var input =  {
11948             tag: 'input',
11949             id : id,
11950             type : this.inputType,
11951             cls : 'form-control',
11952             autocomplete: 'new-password',
11953             placeholder : this.placeholder || '' 
11954             
11955         };
11956         if (this.name) {
11957             input.name = this.name;
11958         }
11959         if (this.size) {
11960             input.cls += ' input-' + this.size;
11961         }
11962         
11963         if (this.disabled) {
11964             input.disabled=true;
11965         }
11966         
11967         var inputblock = input;
11968         
11969         if(this.hasFeedback && !this.allowBlank){
11970             
11971             var feedback = {
11972                 tag: 'span',
11973                 cls: 'glyphicon form-control-feedback'
11974             };
11975             
11976             if(this.removable && !this.editable  ){
11977                 inputblock = {
11978                     cls : 'has-feedback',
11979                     cn :  [
11980                         inputblock,
11981                         {
11982                             tag: 'button',
11983                             html : 'x',
11984                             cls : 'roo-combo-removable-btn close'
11985                         },
11986                         feedback
11987                     ] 
11988                 };
11989             } else {
11990                 inputblock = {
11991                     cls : 'has-feedback',
11992                     cn :  [
11993                         inputblock,
11994                         feedback
11995                     ] 
11996                 };
11997             }
11998
11999         } else {
12000             if(this.removable && !this.editable ){
12001                 inputblock = {
12002                     cls : 'roo-removable',
12003                     cn :  [
12004                         inputblock,
12005                         {
12006                             tag: 'button',
12007                             html : 'x',
12008                             cls : 'roo-combo-removable-btn close'
12009                         }
12010                     ] 
12011                 };
12012             }
12013         }
12014         
12015         if (this.before || this.after) {
12016             
12017             inputblock = {
12018                 cls : 'input-group',
12019                 cn :  [] 
12020             };
12021             if (this.before) {
12022                 inputblock.cn.push({
12023                     tag :'span',
12024                     cls : 'input-group-addon input-group-prepend input-group-text',
12025                     html : this.before
12026                 });
12027             }
12028             
12029             inputblock.cn.push(input);
12030             
12031             if(this.hasFeedback && !this.allowBlank){
12032                 inputblock.cls += ' has-feedback';
12033                 inputblock.cn.push(feedback);
12034             }
12035             
12036             if (this.after) {
12037                 inputblock.cn.push({
12038                     tag :'span',
12039                     cls : 'input-group-addon input-group-append input-group-text',
12040                     html : this.after
12041                 });
12042             }
12043             
12044         };
12045         
12046       
12047         
12048         var ibwrap = inputblock;
12049         
12050         if(this.multiple){
12051             ibwrap = {
12052                 tag: 'ul',
12053                 cls: 'roo-select2-choices',
12054                 cn:[
12055                     {
12056                         tag: 'li',
12057                         cls: 'roo-select2-search-field',
12058                         cn: [
12059
12060                             inputblock
12061                         ]
12062                     }
12063                 ]
12064             };
12065                 
12066         }
12067         
12068         var combobox = {
12069             cls: 'roo-select2-container input-group',
12070             cn: [
12071                  {
12072                     tag: 'input',
12073                     type : 'hidden',
12074                     cls: 'form-hidden-field'
12075                 },
12076                 ibwrap
12077             ]
12078         };
12079         
12080         if(!this.multiple && this.showToggleBtn){
12081             
12082             var caret = {
12083                         tag: 'span',
12084                         cls: 'caret'
12085              };
12086             if (this.caret != false) {
12087                 caret = {
12088                      tag: 'i',
12089                      cls: 'fa fa-' + this.caret
12090                 };
12091                 
12092             }
12093             
12094             combobox.cn.push({
12095                 tag :'span',
12096                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12097                 cn : [
12098                     Roo.bootstrap.version == 3 ? caret : '',
12099                     {
12100                         tag: 'span',
12101                         cls: 'combobox-clear',
12102                         cn  : [
12103                             {
12104                                 tag : 'i',
12105                                 cls: 'icon-remove'
12106                             }
12107                         ]
12108                     }
12109                 ]
12110
12111             })
12112         }
12113         
12114         if(this.multiple){
12115             combobox.cls += ' roo-select2-container-multi';
12116         }
12117          var indicator = {
12118             tag : 'i',
12119             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12120             tooltip : 'This field is required'
12121         };
12122         if (Roo.bootstrap.version == 4) {
12123             indicator = {
12124                 tag : 'i',
12125                 style : 'display:none'
12126             };
12127         }
12128         
12129         
12130         if (align ==='left' && this.fieldLabel.length) {
12131             
12132             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12133
12134             cfg.cn = [
12135                 indicator,
12136                 {
12137                     tag: 'label',
12138                     'for' :  id,
12139                     cls : 'control-label',
12140                     html : this.fieldLabel
12141
12142                 },
12143                 {
12144                     cls : "", 
12145                     cn: [
12146                         combobox
12147                     ]
12148                 }
12149
12150             ];
12151             
12152             var labelCfg = cfg.cn[1];
12153             var contentCfg = cfg.cn[2];
12154             
12155             if(this.indicatorpos == 'right'){
12156                 cfg.cn = [
12157                     {
12158                         tag: 'label',
12159                         'for' :  id,
12160                         cls : 'control-label',
12161                         cn : [
12162                             {
12163                                 tag : 'span',
12164                                 html : this.fieldLabel
12165                             },
12166                             indicator
12167                         ]
12168                     },
12169                     {
12170                         cls : "", 
12171                         cn: [
12172                             combobox
12173                         ]
12174                     }
12175
12176                 ];
12177                 
12178                 labelCfg = cfg.cn[0];
12179                 contentCfg = cfg.cn[1];
12180             }
12181             
12182             if(this.labelWidth > 12){
12183                 labelCfg.style = "width: " + this.labelWidth + 'px';
12184             }
12185             
12186             if(this.labelWidth < 13 && this.labelmd == 0){
12187                 this.labelmd = this.labelWidth;
12188             }
12189             
12190             if(this.labellg > 0){
12191                 labelCfg.cls += ' col-lg-' + this.labellg;
12192                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12193             }
12194             
12195             if(this.labelmd > 0){
12196                 labelCfg.cls += ' col-md-' + this.labelmd;
12197                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12198             }
12199             
12200             if(this.labelsm > 0){
12201                 labelCfg.cls += ' col-sm-' + this.labelsm;
12202                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12203             }
12204             
12205             if(this.labelxs > 0){
12206                 labelCfg.cls += ' col-xs-' + this.labelxs;
12207                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12208             }
12209             
12210         } else if ( this.fieldLabel.length) {
12211 //                Roo.log(" label");
12212             cfg.cn = [
12213                 indicator,
12214                {
12215                    tag: 'label',
12216                    //cls : 'input-group-addon',
12217                    html : this.fieldLabel
12218
12219                },
12220
12221                combobox
12222
12223             ];
12224             
12225             if(this.indicatorpos == 'right'){
12226                 
12227                 cfg.cn = [
12228                     {
12229                        tag: 'label',
12230                        cn : [
12231                            {
12232                                tag : 'span',
12233                                html : this.fieldLabel
12234                            },
12235                            indicator
12236                        ]
12237
12238                     },
12239                     combobox
12240
12241                 ];
12242
12243             }
12244
12245         } else {
12246             
12247 //                Roo.log(" no label && no align");
12248                 cfg = combobox
12249                      
12250                 
12251         }
12252         
12253         var settings=this;
12254         ['xs','sm','md','lg'].map(function(size){
12255             if (settings[size]) {
12256                 cfg.cls += ' col-' + size + '-' + settings[size];
12257             }
12258         });
12259         
12260         return cfg;
12261         
12262     },
12263     
12264     
12265     
12266     // private
12267     onResize : function(w, h){
12268 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12269 //        if(typeof w == 'number'){
12270 //            var x = w - this.trigger.getWidth();
12271 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12272 //            this.trigger.setStyle('left', x+'px');
12273 //        }
12274     },
12275
12276     // private
12277     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12278
12279     // private
12280     getResizeEl : function(){
12281         return this.inputEl();
12282     },
12283
12284     // private
12285     getPositionEl : function(){
12286         return this.inputEl();
12287     },
12288
12289     // private
12290     alignErrorIcon : function(){
12291         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12292     },
12293
12294     // private
12295     initEvents : function(){
12296         
12297         this.createList();
12298         
12299         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12300         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12301         if(!this.multiple && this.showToggleBtn){
12302             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12303             if(this.hideTrigger){
12304                 this.trigger.setDisplayed(false);
12305             }
12306             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12307         }
12308         
12309         if(this.multiple){
12310             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12311         }
12312         
12313         if(this.removable && !this.editable && !this.tickable){
12314             var close = this.closeTriggerEl();
12315             
12316             if(close){
12317                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12318                 close.on('click', this.removeBtnClick, this, close);
12319             }
12320         }
12321         
12322         //this.trigger.addClassOnOver('x-form-trigger-over');
12323         //this.trigger.addClassOnClick('x-form-trigger-click');
12324         
12325         //if(!this.width){
12326         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12327         //}
12328     },
12329     
12330     closeTriggerEl : function()
12331     {
12332         var close = this.el.select('.roo-combo-removable-btn', true).first();
12333         return close ? close : false;
12334     },
12335     
12336     removeBtnClick : function(e, h, el)
12337     {
12338         e.preventDefault();
12339         
12340         if(this.fireEvent("remove", this) !== false){
12341             this.reset();
12342             this.fireEvent("afterremove", this)
12343         }
12344     },
12345     
12346     createList : function()
12347     {
12348         this.list = Roo.get(document.body).createChild({
12349             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12350             cls: 'typeahead typeahead-long dropdown-menu shadow',
12351             style: 'display:none'
12352         });
12353         
12354         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12355         
12356     },
12357
12358     // private
12359     initTrigger : function(){
12360        
12361     },
12362
12363     // private
12364     onDestroy : function(){
12365         if(this.trigger){
12366             this.trigger.removeAllListeners();
12367           //  this.trigger.remove();
12368         }
12369         //if(this.wrap){
12370         //    this.wrap.remove();
12371         //}
12372         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12373     },
12374
12375     // private
12376     onFocus : function(){
12377         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12378         /*
12379         if(!this.mimicing){
12380             this.wrap.addClass('x-trigger-wrap-focus');
12381             this.mimicing = true;
12382             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12383             if(this.monitorTab){
12384                 this.el.on("keydown", this.checkTab, this);
12385             }
12386         }
12387         */
12388     },
12389
12390     // private
12391     checkTab : function(e){
12392         if(e.getKey() == e.TAB){
12393             this.triggerBlur();
12394         }
12395     },
12396
12397     // private
12398     onBlur : function(){
12399         // do nothing
12400     },
12401
12402     // private
12403     mimicBlur : function(e, t){
12404         /*
12405         if(!this.wrap.contains(t) && this.validateBlur()){
12406             this.triggerBlur();
12407         }
12408         */
12409     },
12410
12411     // private
12412     triggerBlur : function(){
12413         this.mimicing = false;
12414         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12415         if(this.monitorTab){
12416             this.el.un("keydown", this.checkTab, this);
12417         }
12418         //this.wrap.removeClass('x-trigger-wrap-focus');
12419         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12420     },
12421
12422     // private
12423     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12424     validateBlur : function(e, t){
12425         return true;
12426     },
12427
12428     // private
12429     onDisable : function(){
12430         this.inputEl().dom.disabled = true;
12431         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12432         //if(this.wrap){
12433         //    this.wrap.addClass('x-item-disabled');
12434         //}
12435     },
12436
12437     // private
12438     onEnable : function(){
12439         this.inputEl().dom.disabled = false;
12440         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12441         //if(this.wrap){
12442         //    this.el.removeClass('x-item-disabled');
12443         //}
12444     },
12445
12446     // private
12447     onShow : function(){
12448         var ae = this.getActionEl();
12449         
12450         if(ae){
12451             ae.dom.style.display = '';
12452             ae.dom.style.visibility = 'visible';
12453         }
12454     },
12455
12456     // private
12457     
12458     onHide : function(){
12459         var ae = this.getActionEl();
12460         ae.dom.style.display = 'none';
12461     },
12462
12463     /**
12464      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12465      * by an implementing function.
12466      * @method
12467      * @param {EventObject} e
12468      */
12469     onTriggerClick : Roo.emptyFn
12470 });
12471  
12472 /*
12473 * Licence: LGPL
12474 */
12475
12476 /**
12477  * @class Roo.bootstrap.CardUploader
12478  * @extends Roo.bootstrap.Button
12479  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12480  * @cfg {Number} errorTimeout default 3000
12481  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12482  * @cfg {Array}  html The button text.
12483
12484  *
12485  * @constructor
12486  * Create a new CardUploader
12487  * @param {Object} config The config object
12488  */
12489
12490 Roo.bootstrap.CardUploader = function(config){
12491     
12492  
12493     
12494     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12495     
12496     
12497     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12498         return r.data.id
12499         });
12500     
12501     
12502 };
12503
12504 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12505     
12506      
12507     errorTimeout : 3000,
12508      
12509     images : false,
12510    
12511     fileCollection : false,
12512     allowBlank : true,
12513     
12514     getAutoCreate : function()
12515     {
12516         
12517         var cfg =  {
12518             cls :'form-group' ,
12519             cn : [
12520                
12521                 {
12522                     tag: 'label',
12523                    //cls : 'input-group-addon',
12524                     html : this.fieldLabel
12525
12526                 },
12527
12528                 {
12529                     tag: 'input',
12530                     type : 'hidden',
12531                     nane : this.name,
12532                     value : this.value,
12533                     cls : 'd-none  form-control'
12534                 },
12535                 
12536                 {
12537                     tag: 'input',
12538                     multiple : 'multiple',
12539                     type : 'file',
12540                     cls : 'd-none  roo-card-upload-selector'
12541                 },
12542                 
12543                 {
12544                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12545                 },
12546                 {
12547                     cls : 'card-columns roo-card-uploader-container'
12548                 }
12549
12550             ]
12551         };
12552            
12553          
12554         return cfg;
12555     },
12556     
12557     getChildContainer : function() /// what children are added to.
12558     {
12559         return this.containerEl;
12560     },
12561    
12562     getButtonContainer : function() /// what children are added to.
12563     {
12564         return this.el.select(".roo-card-uploader-button-container").first();
12565     },
12566    
12567     initEvents : function()
12568     {
12569         
12570         Roo.bootstrap.Input.prototype.initEvents.call(this);
12571         
12572         var t = this;
12573         this.addxtype({
12574             xns: Roo.bootstrap,
12575
12576             xtype : 'Button',
12577             container_method : 'getButtonContainer' ,            
12578             html :  this.html, // fix changable?
12579             cls : 'w-100 ',
12580             listeners : {
12581                 'click' : function(btn, e) {
12582                     t.onClick(e);
12583                 }
12584             }
12585         });
12586         
12587         
12588         
12589         
12590         this.urlAPI = (window.createObjectURL && window) || 
12591                                 (window.URL && URL.revokeObjectURL && URL) || 
12592                                 (window.webkitURL && webkitURL);
12593                         
12594          
12595          
12596          
12597         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12598         
12599         this.selectorEl.on('change', this.onFileSelected, this);
12600         if (this.images) {
12601             var t = this;
12602             this.images.forEach(function(img) {
12603                 t.addCard(img)
12604             });
12605             this.images = false;
12606         }
12607         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12608          
12609        
12610     },
12611     
12612    
12613     onClick : function(e)
12614     {
12615         e.preventDefault();
12616          
12617         this.selectorEl.dom.click();
12618          
12619     },
12620     
12621     onFileSelected : function(e)
12622     {
12623         e.preventDefault();
12624         
12625         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12626             return;
12627         }
12628         
12629         Roo.each(this.selectorEl.dom.files, function(file){    
12630             this.addFile(file);
12631         }, this);
12632          
12633     },
12634     
12635       
12636     
12637       
12638     
12639     addFile : function(file)
12640     {
12641            
12642         if(typeof(file) === 'string'){
12643             throw "Add file by name?"; // should not happen
12644             return;
12645         }
12646         
12647         if(!file || !this.urlAPI){
12648             return;
12649         }
12650         
12651         // file;
12652         // file.type;
12653         
12654         var _this = this;
12655         
12656         
12657         var url = _this.urlAPI.createObjectURL( file);
12658            
12659         this.addCard({
12660             id : Roo.bootstrap.CardUploader.ID--,
12661             is_uploaded : false,
12662             src : url,
12663             title : file.name,
12664             mimetype : file.type,
12665             preview : false,
12666             is_deleted : 0
12667         })
12668         
12669     },
12670     
12671     addCard : function (data)
12672     {
12673         // hidden input element?
12674         // if the file is not an image...
12675         //then we need to use something other that and header_image
12676         var t = this;
12677         //   remove.....
12678         var footer = [
12679             {
12680                 xns : Roo.bootstrap,
12681                 xtype : 'CardFooter',
12682                 items: [
12683                     {
12684                         xns : Roo.bootstrap,
12685                         xtype : 'Element',
12686                         cls : 'd-flex',
12687                         items : [
12688                             
12689                             {
12690                                 xns : Roo.bootstrap,
12691                                 xtype : 'Button',
12692                                 html : String.format("<small>{0}</small>", data.title),
12693                                 cls : 'col-11 text-left',
12694                                 size: 'sm',
12695                                 weight: 'link',
12696                                 fa : 'download',
12697                                 listeners : {
12698                                     click : function() {
12699                                         this.downloadCard(data.id)
12700                                     }
12701                                 }
12702                             },
12703                           
12704                             {
12705                                 xns : Roo.bootstrap,
12706                                 xtype : 'Button',
12707                                 
12708                                 size : 'sm',
12709                                 weight: 'danger',
12710                                 cls : 'col-1',
12711                                 fa : 'times',
12712                                 listeners : {
12713                                     click : function() {
12714                                         t.removeCard(data.id)
12715                                     }
12716                                 }
12717                             }
12718                         ]
12719                     }
12720                     
12721                 ] 
12722             }
12723             
12724         ];
12725
12726         var cn = this.addxtype(
12727             {
12728                  
12729                 xns : Roo.bootstrap,
12730                 xtype : 'Card',
12731                 closeable : true,
12732                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12733                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
12734                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
12735                 data : data,
12736                 html : false,
12737                  
12738                 items : footer,
12739                 initEvents : function() {
12740                     Roo.bootstrap.Card.prototype.initEvents.call(this);
12741                     this.imgEl = this.el.select('.card-img-top').first();
12742                     if (this.imgEl) {
12743                         this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12744                         this.imgEl.set({ 'pointer' : 'cursor' });
12745                                   
12746                     }
12747                     
12748                   
12749                 }
12750                 
12751             }
12752         );
12753         // dont' really need ot update items.
12754         // this.items.push(cn);
12755         this.fileCollection.add(cn);
12756         this.updateInput();
12757         
12758     },
12759     removeCard : function(id)
12760     {
12761         
12762         var card  = this.fileCollection.get(id);
12763         card.data.is_deleted = 1;
12764         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12765         this.fileCollection.remove(card);
12766         //this.items = this.items.filter(function(e) { return e != card });
12767         // dont' really need ot update items.
12768         card.el.dom.parentNode.removeChild(card.el.dom);
12769         
12770     },
12771     reset: function()
12772     {
12773         this.fileCollection.each(function(card) {
12774             card.el.dom.parentNode.removeChild(card.el.dom);    
12775         });
12776         this.fileCollection.clear();
12777         this.updateInput();
12778     },
12779     
12780     updateInput : function()
12781     {
12782         var i =0;
12783         var data = [];
12784         var dom = this.inputEl().dom;
12785         var fc = this.fileCollection;
12786         var next = function() {
12787             if (i >= fc.length) {
12788                 dom.value = JSON.stringify(data);
12789                 return;
12790             }
12791             var reader = new FileReader();
12792             reader.onloadend = function(evt) {  
12793                 // file is loaded
12794                 var ee = Roo.apply({}, fc[i]);
12795                 ee.src = evt.target.result;
12796                 data.push(ee);
12797                 
12798             };
12799             reader.readAsDataURL(fc[i].src); 
12800             
12801         }
12802         next();
12803         
12804         
12805     }
12806     
12807     
12808 });
12809
12810
12811 Roo.bootstrap.CardUploader.ID = -1;/*
12812  * Based on:
12813  * Ext JS Library 1.1.1
12814  * Copyright(c) 2006-2007, Ext JS, LLC.
12815  *
12816  * Originally Released Under LGPL - original licence link has changed is not relivant.
12817  *
12818  * Fork - LGPL
12819  * <script type="text/javascript">
12820  */
12821
12822
12823 /**
12824  * @class Roo.data.SortTypes
12825  * @singleton
12826  * Defines the default sorting (casting?) comparison functions used when sorting data.
12827  */
12828 Roo.data.SortTypes = {
12829     /**
12830      * Default sort that does nothing
12831      * @param {Mixed} s The value being converted
12832      * @return {Mixed} The comparison value
12833      */
12834     none : function(s){
12835         return s;
12836     },
12837     
12838     /**
12839      * The regular expression used to strip tags
12840      * @type {RegExp}
12841      * @property
12842      */
12843     stripTagsRE : /<\/?[^>]+>/gi,
12844     
12845     /**
12846      * Strips all HTML tags to sort on text only
12847      * @param {Mixed} s The value being converted
12848      * @return {String} The comparison value
12849      */
12850     asText : function(s){
12851         return String(s).replace(this.stripTagsRE, "");
12852     },
12853     
12854     /**
12855      * Strips all HTML tags to sort on text only - Case insensitive
12856      * @param {Mixed} s The value being converted
12857      * @return {String} The comparison value
12858      */
12859     asUCText : function(s){
12860         return String(s).toUpperCase().replace(this.stripTagsRE, "");
12861     },
12862     
12863     /**
12864      * Case insensitive string
12865      * @param {Mixed} s The value being converted
12866      * @return {String} The comparison value
12867      */
12868     asUCString : function(s) {
12869         return String(s).toUpperCase();
12870     },
12871     
12872     /**
12873      * Date sorting
12874      * @param {Mixed} s The value being converted
12875      * @return {Number} The comparison value
12876      */
12877     asDate : function(s) {
12878         if(!s){
12879             return 0;
12880         }
12881         if(s instanceof Date){
12882             return s.getTime();
12883         }
12884         return Date.parse(String(s));
12885     },
12886     
12887     /**
12888      * Float sorting
12889      * @param {Mixed} s The value being converted
12890      * @return {Float} The comparison value
12891      */
12892     asFloat : function(s) {
12893         var val = parseFloat(String(s).replace(/,/g, ""));
12894         if(isNaN(val)) {
12895             val = 0;
12896         }
12897         return val;
12898     },
12899     
12900     /**
12901      * Integer sorting
12902      * @param {Mixed} s The value being converted
12903      * @return {Number} The comparison value
12904      */
12905     asInt : function(s) {
12906         var val = parseInt(String(s).replace(/,/g, ""));
12907         if(isNaN(val)) {
12908             val = 0;
12909         }
12910         return val;
12911     }
12912 };/*
12913  * Based on:
12914  * Ext JS Library 1.1.1
12915  * Copyright(c) 2006-2007, Ext JS, LLC.
12916  *
12917  * Originally Released Under LGPL - original licence link has changed is not relivant.
12918  *
12919  * Fork - LGPL
12920  * <script type="text/javascript">
12921  */
12922
12923 /**
12924 * @class Roo.data.Record
12925  * Instances of this class encapsulate both record <em>definition</em> information, and record
12926  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12927  * to access Records cached in an {@link Roo.data.Store} object.<br>
12928  * <p>
12929  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12930  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12931  * objects.<br>
12932  * <p>
12933  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12934  * @constructor
12935  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12936  * {@link #create}. The parameters are the same.
12937  * @param {Array} data An associative Array of data values keyed by the field name.
12938  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12939  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12940  * not specified an integer id is generated.
12941  */
12942 Roo.data.Record = function(data, id){
12943     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12944     this.data = data;
12945 };
12946
12947 /**
12948  * Generate a constructor for a specific record layout.
12949  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12950  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12951  * Each field definition object may contain the following properties: <ul>
12952  * <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,
12953  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12954  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12955  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12956  * is being used, then this is a string containing the javascript expression to reference the data relative to 
12957  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12958  * to the data item relative to the record element. If the mapping expression is the same as the field name,
12959  * this may be omitted.</p></li>
12960  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12961  * <ul><li>auto (Default, implies no conversion)</li>
12962  * <li>string</li>
12963  * <li>int</li>
12964  * <li>float</li>
12965  * <li>boolean</li>
12966  * <li>date</li></ul></p></li>
12967  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12968  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12969  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12970  * by the Reader into an object that will be stored in the Record. It is passed the
12971  * following parameters:<ul>
12972  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12973  * </ul></p></li>
12974  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12975  * </ul>
12976  * <br>usage:<br><pre><code>
12977 var TopicRecord = Roo.data.Record.create(
12978     {name: 'title', mapping: 'topic_title'},
12979     {name: 'author', mapping: 'username'},
12980     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12981     {name: 'lastPost', mapping: 'post_time', type: 'date'},
12982     {name: 'lastPoster', mapping: 'user2'},
12983     {name: 'excerpt', mapping: 'post_text'}
12984 );
12985
12986 var myNewRecord = new TopicRecord({
12987     title: 'Do my job please',
12988     author: 'noobie',
12989     totalPosts: 1,
12990     lastPost: new Date(),
12991     lastPoster: 'Animal',
12992     excerpt: 'No way dude!'
12993 });
12994 myStore.add(myNewRecord);
12995 </code></pre>
12996  * @method create
12997  * @static
12998  */
12999 Roo.data.Record.create = function(o){
13000     var f = function(){
13001         f.superclass.constructor.apply(this, arguments);
13002     };
13003     Roo.extend(f, Roo.data.Record);
13004     var p = f.prototype;
13005     p.fields = new Roo.util.MixedCollection(false, function(field){
13006         return field.name;
13007     });
13008     for(var i = 0, len = o.length; i < len; i++){
13009         p.fields.add(new Roo.data.Field(o[i]));
13010     }
13011     f.getField = function(name){
13012         return p.fields.get(name);  
13013     };
13014     return f;
13015 };
13016
13017 Roo.data.Record.AUTO_ID = 1000;
13018 Roo.data.Record.EDIT = 'edit';
13019 Roo.data.Record.REJECT = 'reject';
13020 Roo.data.Record.COMMIT = 'commit';
13021
13022 Roo.data.Record.prototype = {
13023     /**
13024      * Readonly flag - true if this record has been modified.
13025      * @type Boolean
13026      */
13027     dirty : false,
13028     editing : false,
13029     error: null,
13030     modified: null,
13031
13032     // private
13033     join : function(store){
13034         this.store = store;
13035     },
13036
13037     /**
13038      * Set the named field to the specified value.
13039      * @param {String} name The name of the field to set.
13040      * @param {Object} value The value to set the field to.
13041      */
13042     set : function(name, value){
13043         if(this.data[name] == value){
13044             return;
13045         }
13046         this.dirty = true;
13047         if(!this.modified){
13048             this.modified = {};
13049         }
13050         if(typeof this.modified[name] == 'undefined'){
13051             this.modified[name] = this.data[name];
13052         }
13053         this.data[name] = value;
13054         if(!this.editing && this.store){
13055             this.store.afterEdit(this);
13056         }       
13057     },
13058
13059     /**
13060      * Get the value of the named field.
13061      * @param {String} name The name of the field to get the value of.
13062      * @return {Object} The value of the field.
13063      */
13064     get : function(name){
13065         return this.data[name]; 
13066     },
13067
13068     // private
13069     beginEdit : function(){
13070         this.editing = true;
13071         this.modified = {}; 
13072     },
13073
13074     // private
13075     cancelEdit : function(){
13076         this.editing = false;
13077         delete this.modified;
13078     },
13079
13080     // private
13081     endEdit : function(){
13082         this.editing = false;
13083         if(this.dirty && this.store){
13084             this.store.afterEdit(this);
13085         }
13086     },
13087
13088     /**
13089      * Usually called by the {@link Roo.data.Store} which owns the Record.
13090      * Rejects all changes made to the Record since either creation, or the last commit operation.
13091      * Modified fields are reverted to their original values.
13092      * <p>
13093      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13094      * of reject operations.
13095      */
13096     reject : function(){
13097         var m = this.modified;
13098         for(var n in m){
13099             if(typeof m[n] != "function"){
13100                 this.data[n] = m[n];
13101             }
13102         }
13103         this.dirty = false;
13104         delete this.modified;
13105         this.editing = false;
13106         if(this.store){
13107             this.store.afterReject(this);
13108         }
13109     },
13110
13111     /**
13112      * Usually called by the {@link Roo.data.Store} which owns the Record.
13113      * Commits all changes made to the Record since either creation, or the last commit operation.
13114      * <p>
13115      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13116      * of commit operations.
13117      */
13118     commit : function(){
13119         this.dirty = false;
13120         delete this.modified;
13121         this.editing = false;
13122         if(this.store){
13123             this.store.afterCommit(this);
13124         }
13125     },
13126
13127     // private
13128     hasError : function(){
13129         return this.error != null;
13130     },
13131
13132     // private
13133     clearError : function(){
13134         this.error = null;
13135     },
13136
13137     /**
13138      * Creates a copy of this record.
13139      * @param {String} id (optional) A new record id if you don't want to use this record's id
13140      * @return {Record}
13141      */
13142     copy : function(newId) {
13143         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13144     }
13145 };/*
13146  * Based on:
13147  * Ext JS Library 1.1.1
13148  * Copyright(c) 2006-2007, Ext JS, LLC.
13149  *
13150  * Originally Released Under LGPL - original licence link has changed is not relivant.
13151  *
13152  * Fork - LGPL
13153  * <script type="text/javascript">
13154  */
13155
13156
13157
13158 /**
13159  * @class Roo.data.Store
13160  * @extends Roo.util.Observable
13161  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13162  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13163  * <p>
13164  * 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
13165  * has no knowledge of the format of the data returned by the Proxy.<br>
13166  * <p>
13167  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13168  * instances from the data object. These records are cached and made available through accessor functions.
13169  * @constructor
13170  * Creates a new Store.
13171  * @param {Object} config A config object containing the objects needed for the Store to access data,
13172  * and read the data into Records.
13173  */
13174 Roo.data.Store = function(config){
13175     this.data = new Roo.util.MixedCollection(false);
13176     this.data.getKey = function(o){
13177         return o.id;
13178     };
13179     this.baseParams = {};
13180     // private
13181     this.paramNames = {
13182         "start" : "start",
13183         "limit" : "limit",
13184         "sort" : "sort",
13185         "dir" : "dir",
13186         "multisort" : "_multisort"
13187     };
13188
13189     if(config && config.data){
13190         this.inlineData = config.data;
13191         delete config.data;
13192     }
13193
13194     Roo.apply(this, config);
13195     
13196     if(this.reader){ // reader passed
13197         this.reader = Roo.factory(this.reader, Roo.data);
13198         this.reader.xmodule = this.xmodule || false;
13199         if(!this.recordType){
13200             this.recordType = this.reader.recordType;
13201         }
13202         if(this.reader.onMetaChange){
13203             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13204         }
13205     }
13206
13207     if(this.recordType){
13208         this.fields = this.recordType.prototype.fields;
13209     }
13210     this.modified = [];
13211
13212     this.addEvents({
13213         /**
13214          * @event datachanged
13215          * Fires when the data cache has changed, and a widget which is using this Store
13216          * as a Record cache should refresh its view.
13217          * @param {Store} this
13218          */
13219         datachanged : true,
13220         /**
13221          * @event metachange
13222          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13223          * @param {Store} this
13224          * @param {Object} meta The JSON metadata
13225          */
13226         metachange : true,
13227         /**
13228          * @event add
13229          * Fires when Records have been added to the Store
13230          * @param {Store} this
13231          * @param {Roo.data.Record[]} records The array of Records added
13232          * @param {Number} index The index at which the record(s) were added
13233          */
13234         add : true,
13235         /**
13236          * @event remove
13237          * Fires when a Record has been removed from the Store
13238          * @param {Store} this
13239          * @param {Roo.data.Record} record The Record that was removed
13240          * @param {Number} index The index at which the record was removed
13241          */
13242         remove : true,
13243         /**
13244          * @event update
13245          * Fires when a Record has been updated
13246          * @param {Store} this
13247          * @param {Roo.data.Record} record The Record that was updated
13248          * @param {String} operation The update operation being performed.  Value may be one of:
13249          * <pre><code>
13250  Roo.data.Record.EDIT
13251  Roo.data.Record.REJECT
13252  Roo.data.Record.COMMIT
13253          * </code></pre>
13254          */
13255         update : true,
13256         /**
13257          * @event clear
13258          * Fires when the data cache has been cleared.
13259          * @param {Store} this
13260          */
13261         clear : true,
13262         /**
13263          * @event beforeload
13264          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13265          * the load action will be canceled.
13266          * @param {Store} this
13267          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13268          */
13269         beforeload : true,
13270         /**
13271          * @event beforeloadadd
13272          * Fires after a new set of Records has been loaded.
13273          * @param {Store} this
13274          * @param {Roo.data.Record[]} records The Records that were loaded
13275          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13276          */
13277         beforeloadadd : true,
13278         /**
13279          * @event load
13280          * Fires after a new set of Records has been loaded, before they are added to the store.
13281          * @param {Store} this
13282          * @param {Roo.data.Record[]} records The Records that were loaded
13283          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13284          * @params {Object} return from reader
13285          */
13286         load : true,
13287         /**
13288          * @event loadexception
13289          * Fires if an exception occurs in the Proxy during loading.
13290          * Called with the signature of the Proxy's "loadexception" event.
13291          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13292          * 
13293          * @param {Proxy} 
13294          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13295          * @param {Object} load options 
13296          * @param {Object} jsonData from your request (normally this contains the Exception)
13297          */
13298         loadexception : true
13299     });
13300     
13301     if(this.proxy){
13302         this.proxy = Roo.factory(this.proxy, Roo.data);
13303         this.proxy.xmodule = this.xmodule || false;
13304         this.relayEvents(this.proxy,  ["loadexception"]);
13305     }
13306     this.sortToggle = {};
13307     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13308
13309     Roo.data.Store.superclass.constructor.call(this);
13310
13311     if(this.inlineData){
13312         this.loadData(this.inlineData);
13313         delete this.inlineData;
13314     }
13315 };
13316
13317 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13318      /**
13319     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13320     * without a remote query - used by combo/forms at present.
13321     */
13322     
13323     /**
13324     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13325     */
13326     /**
13327     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13328     */
13329     /**
13330     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13331     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13332     */
13333     /**
13334     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13335     * on any HTTP request
13336     */
13337     /**
13338     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13339     */
13340     /**
13341     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13342     */
13343     multiSort: false,
13344     /**
13345     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13346     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13347     */
13348     remoteSort : false,
13349
13350     /**
13351     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13352      * loaded or when a record is removed. (defaults to false).
13353     */
13354     pruneModifiedRecords : false,
13355
13356     // private
13357     lastOptions : null,
13358
13359     /**
13360      * Add Records to the Store and fires the add event.
13361      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13362      */
13363     add : function(records){
13364         records = [].concat(records);
13365         for(var i = 0, len = records.length; i < len; i++){
13366             records[i].join(this);
13367         }
13368         var index = this.data.length;
13369         this.data.addAll(records);
13370         this.fireEvent("add", this, records, index);
13371     },
13372
13373     /**
13374      * Remove a Record from the Store and fires the remove event.
13375      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13376      */
13377     remove : function(record){
13378         var index = this.data.indexOf(record);
13379         this.data.removeAt(index);
13380  
13381         if(this.pruneModifiedRecords){
13382             this.modified.remove(record);
13383         }
13384         this.fireEvent("remove", this, record, index);
13385     },
13386
13387     /**
13388      * Remove all Records from the Store and fires the clear event.
13389      */
13390     removeAll : function(){
13391         this.data.clear();
13392         if(this.pruneModifiedRecords){
13393             this.modified = [];
13394         }
13395         this.fireEvent("clear", this);
13396     },
13397
13398     /**
13399      * Inserts Records to the Store at the given index and fires the add event.
13400      * @param {Number} index The start index at which to insert the passed Records.
13401      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13402      */
13403     insert : function(index, records){
13404         records = [].concat(records);
13405         for(var i = 0, len = records.length; i < len; i++){
13406             this.data.insert(index, records[i]);
13407             records[i].join(this);
13408         }
13409         this.fireEvent("add", this, records, index);
13410     },
13411
13412     /**
13413      * Get the index within the cache of the passed Record.
13414      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13415      * @return {Number} The index of the passed Record. Returns -1 if not found.
13416      */
13417     indexOf : function(record){
13418         return this.data.indexOf(record);
13419     },
13420
13421     /**
13422      * Get the index within the cache of the Record with the passed id.
13423      * @param {String} id The id of the Record to find.
13424      * @return {Number} The index of the Record. Returns -1 if not found.
13425      */
13426     indexOfId : function(id){
13427         return this.data.indexOfKey(id);
13428     },
13429
13430     /**
13431      * Get the Record with the specified id.
13432      * @param {String} id The id of the Record to find.
13433      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13434      */
13435     getById : function(id){
13436         return this.data.key(id);
13437     },
13438
13439     /**
13440      * Get the Record at the specified index.
13441      * @param {Number} index The index of the Record to find.
13442      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13443      */
13444     getAt : function(index){
13445         return this.data.itemAt(index);
13446     },
13447
13448     /**
13449      * Returns a range of Records between specified indices.
13450      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13451      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13452      * @return {Roo.data.Record[]} An array of Records
13453      */
13454     getRange : function(start, end){
13455         return this.data.getRange(start, end);
13456     },
13457
13458     // private
13459     storeOptions : function(o){
13460         o = Roo.apply({}, o);
13461         delete o.callback;
13462         delete o.scope;
13463         this.lastOptions = o;
13464     },
13465
13466     /**
13467      * Loads the Record cache from the configured Proxy using the configured Reader.
13468      * <p>
13469      * If using remote paging, then the first load call must specify the <em>start</em>
13470      * and <em>limit</em> properties in the options.params property to establish the initial
13471      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13472      * <p>
13473      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13474      * and this call will return before the new data has been loaded. Perform any post-processing
13475      * in a callback function, or in a "load" event handler.</strong>
13476      * <p>
13477      * @param {Object} options An object containing properties which control loading options:<ul>
13478      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13479      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13480      * passed the following arguments:<ul>
13481      * <li>r : Roo.data.Record[]</li>
13482      * <li>options: Options object from the load call</li>
13483      * <li>success: Boolean success indicator</li></ul></li>
13484      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13485      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13486      * </ul>
13487      */
13488     load : function(options){
13489         options = options || {};
13490         if(this.fireEvent("beforeload", this, options) !== false){
13491             this.storeOptions(options);
13492             var p = Roo.apply(options.params || {}, this.baseParams);
13493             // if meta was not loaded from remote source.. try requesting it.
13494             if (!this.reader.metaFromRemote) {
13495                 p._requestMeta = 1;
13496             }
13497             if(this.sortInfo && this.remoteSort){
13498                 var pn = this.paramNames;
13499                 p[pn["sort"]] = this.sortInfo.field;
13500                 p[pn["dir"]] = this.sortInfo.direction;
13501             }
13502             if (this.multiSort) {
13503                 var pn = this.paramNames;
13504                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13505             }
13506             
13507             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13508         }
13509     },
13510
13511     /**
13512      * Reloads the Record cache from the configured Proxy using the configured Reader and
13513      * the options from the last load operation performed.
13514      * @param {Object} options (optional) An object containing properties which may override the options
13515      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13516      * the most recently used options are reused).
13517      */
13518     reload : function(options){
13519         this.load(Roo.applyIf(options||{}, this.lastOptions));
13520     },
13521
13522     // private
13523     // Called as a callback by the Reader during a load operation.
13524     loadRecords : function(o, options, success){
13525         if(!o || success === false){
13526             if(success !== false){
13527                 this.fireEvent("load", this, [], options, o);
13528             }
13529             if(options.callback){
13530                 options.callback.call(options.scope || this, [], options, false);
13531             }
13532             return;
13533         }
13534         // if data returned failure - throw an exception.
13535         if (o.success === false) {
13536             // show a message if no listener is registered.
13537             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13538                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13539             }
13540             // loadmask wil be hooked into this..
13541             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13542             return;
13543         }
13544         var r = o.records, t = o.totalRecords || r.length;
13545         
13546         this.fireEvent("beforeloadadd", this, r, options, o);
13547         
13548         if(!options || options.add !== true){
13549             if(this.pruneModifiedRecords){
13550                 this.modified = [];
13551             }
13552             for(var i = 0, len = r.length; i < len; i++){
13553                 r[i].join(this);
13554             }
13555             if(this.snapshot){
13556                 this.data = this.snapshot;
13557                 delete this.snapshot;
13558             }
13559             this.data.clear();
13560             this.data.addAll(r);
13561             this.totalLength = t;
13562             this.applySort();
13563             this.fireEvent("datachanged", this);
13564         }else{
13565             this.totalLength = Math.max(t, this.data.length+r.length);
13566             this.add(r);
13567         }
13568         
13569         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13570                 
13571             var e = new Roo.data.Record({});
13572
13573             e.set(this.parent.displayField, this.parent.emptyTitle);
13574             e.set(this.parent.valueField, '');
13575
13576             this.insert(0, e);
13577         }
13578             
13579         this.fireEvent("load", this, r, options, o);
13580         if(options.callback){
13581             options.callback.call(options.scope || this, r, options, true);
13582         }
13583     },
13584
13585
13586     /**
13587      * Loads data from a passed data block. A Reader which understands the format of the data
13588      * must have been configured in the constructor.
13589      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13590      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13591      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13592      */
13593     loadData : function(o, append){
13594         var r = this.reader.readRecords(o);
13595         this.loadRecords(r, {add: append}, true);
13596     },
13597     
13598      /**
13599      * using 'cn' the nested child reader read the child array into it's child stores.
13600      * @param {Object} rec The record with a 'children array
13601      */
13602     loadDataFromChildren : function(rec)
13603     {
13604         this.loadData(this.reader.toLoadData(rec));
13605     },
13606     
13607
13608     /**
13609      * Gets the number of cached records.
13610      * <p>
13611      * <em>If using paging, this may not be the total size of the dataset. If the data object
13612      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13613      * the data set size</em>
13614      */
13615     getCount : function(){
13616         return this.data.length || 0;
13617     },
13618
13619     /**
13620      * Gets the total number of records in the dataset as returned by the server.
13621      * <p>
13622      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13623      * the dataset size</em>
13624      */
13625     getTotalCount : function(){
13626         return this.totalLength || 0;
13627     },
13628
13629     /**
13630      * Returns the sort state of the Store as an object with two properties:
13631      * <pre><code>
13632  field {String} The name of the field by which the Records are sorted
13633  direction {String} The sort order, "ASC" or "DESC"
13634      * </code></pre>
13635      */
13636     getSortState : function(){
13637         return this.sortInfo;
13638     },
13639
13640     // private
13641     applySort : function(){
13642         if(this.sortInfo && !this.remoteSort){
13643             var s = this.sortInfo, f = s.field;
13644             var st = this.fields.get(f).sortType;
13645             var fn = function(r1, r2){
13646                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13647                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13648             };
13649             this.data.sort(s.direction, fn);
13650             if(this.snapshot && this.snapshot != this.data){
13651                 this.snapshot.sort(s.direction, fn);
13652             }
13653         }
13654     },
13655
13656     /**
13657      * Sets the default sort column and order to be used by the next load operation.
13658      * @param {String} fieldName The name of the field to sort by.
13659      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13660      */
13661     setDefaultSort : function(field, dir){
13662         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13663     },
13664
13665     /**
13666      * Sort the Records.
13667      * If remote sorting is used, the sort is performed on the server, and the cache is
13668      * reloaded. If local sorting is used, the cache is sorted internally.
13669      * @param {String} fieldName The name of the field to sort by.
13670      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13671      */
13672     sort : function(fieldName, dir){
13673         var f = this.fields.get(fieldName);
13674         if(!dir){
13675             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13676             
13677             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13678                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13679             }else{
13680                 dir = f.sortDir;
13681             }
13682         }
13683         this.sortToggle[f.name] = dir;
13684         this.sortInfo = {field: f.name, direction: dir};
13685         if(!this.remoteSort){
13686             this.applySort();
13687             this.fireEvent("datachanged", this);
13688         }else{
13689             this.load(this.lastOptions);
13690         }
13691     },
13692
13693     /**
13694      * Calls the specified function for each of the Records in the cache.
13695      * @param {Function} fn The function to call. The Record is passed as the first parameter.
13696      * Returning <em>false</em> aborts and exits the iteration.
13697      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13698      */
13699     each : function(fn, scope){
13700         this.data.each(fn, scope);
13701     },
13702
13703     /**
13704      * Gets all records modified since the last commit.  Modified records are persisted across load operations
13705      * (e.g., during paging).
13706      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13707      */
13708     getModifiedRecords : function(){
13709         return this.modified;
13710     },
13711
13712     // private
13713     createFilterFn : function(property, value, anyMatch){
13714         if(!value.exec){ // not a regex
13715             value = String(value);
13716             if(value.length == 0){
13717                 return false;
13718             }
13719             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13720         }
13721         return function(r){
13722             return value.test(r.data[property]);
13723         };
13724     },
13725
13726     /**
13727      * Sums the value of <i>property</i> for each record between start and end and returns the result.
13728      * @param {String} property A field on your records
13729      * @param {Number} start The record index to start at (defaults to 0)
13730      * @param {Number} end The last record index to include (defaults to length - 1)
13731      * @return {Number} The sum
13732      */
13733     sum : function(property, start, end){
13734         var rs = this.data.items, v = 0;
13735         start = start || 0;
13736         end = (end || end === 0) ? end : rs.length-1;
13737
13738         for(var i = start; i <= end; i++){
13739             v += (rs[i].data[property] || 0);
13740         }
13741         return v;
13742     },
13743
13744     /**
13745      * Filter the records by a specified property.
13746      * @param {String} field A field on your records
13747      * @param {String/RegExp} value Either a string that the field
13748      * should start with or a RegExp to test against the field
13749      * @param {Boolean} anyMatch True to match any part not just the beginning
13750      */
13751     filter : function(property, value, anyMatch){
13752         var fn = this.createFilterFn(property, value, anyMatch);
13753         return fn ? this.filterBy(fn) : this.clearFilter();
13754     },
13755
13756     /**
13757      * Filter by a function. The specified function will be called with each
13758      * record in this data source. If the function returns true the record is included,
13759      * otherwise it is filtered.
13760      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13761      * @param {Object} scope (optional) The scope of the function (defaults to this)
13762      */
13763     filterBy : function(fn, scope){
13764         this.snapshot = this.snapshot || this.data;
13765         this.data = this.queryBy(fn, scope||this);
13766         this.fireEvent("datachanged", this);
13767     },
13768
13769     /**
13770      * Query the records by a specified property.
13771      * @param {String} field A field on your records
13772      * @param {String/RegExp} value Either a string that the field
13773      * should start with or a RegExp to test against the field
13774      * @param {Boolean} anyMatch True to match any part not just the beginning
13775      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13776      */
13777     query : function(property, value, anyMatch){
13778         var fn = this.createFilterFn(property, value, anyMatch);
13779         return fn ? this.queryBy(fn) : this.data.clone();
13780     },
13781
13782     /**
13783      * Query by a function. The specified function will be called with each
13784      * record in this data source. If the function returns true the record is included
13785      * in the results.
13786      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13787      * @param {Object} scope (optional) The scope of the function (defaults to this)
13788       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13789      **/
13790     queryBy : function(fn, scope){
13791         var data = this.snapshot || this.data;
13792         return data.filterBy(fn, scope||this);
13793     },
13794
13795     /**
13796      * Collects unique values for a particular dataIndex from this store.
13797      * @param {String} dataIndex The property to collect
13798      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13799      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13800      * @return {Array} An array of the unique values
13801      **/
13802     collect : function(dataIndex, allowNull, bypassFilter){
13803         var d = (bypassFilter === true && this.snapshot) ?
13804                 this.snapshot.items : this.data.items;
13805         var v, sv, r = [], l = {};
13806         for(var i = 0, len = d.length; i < len; i++){
13807             v = d[i].data[dataIndex];
13808             sv = String(v);
13809             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13810                 l[sv] = true;
13811                 r[r.length] = v;
13812             }
13813         }
13814         return r;
13815     },
13816
13817     /**
13818      * Revert to a view of the Record cache with no filtering applied.
13819      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13820      */
13821     clearFilter : function(suppressEvent){
13822         if(this.snapshot && this.snapshot != this.data){
13823             this.data = this.snapshot;
13824             delete this.snapshot;
13825             if(suppressEvent !== true){
13826                 this.fireEvent("datachanged", this);
13827             }
13828         }
13829     },
13830
13831     // private
13832     afterEdit : function(record){
13833         if(this.modified.indexOf(record) == -1){
13834             this.modified.push(record);
13835         }
13836         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13837     },
13838     
13839     // private
13840     afterReject : function(record){
13841         this.modified.remove(record);
13842         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13843     },
13844
13845     // private
13846     afterCommit : function(record){
13847         this.modified.remove(record);
13848         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13849     },
13850
13851     /**
13852      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13853      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13854      */
13855     commitChanges : function(){
13856         var m = this.modified.slice(0);
13857         this.modified = [];
13858         for(var i = 0, len = m.length; i < len; i++){
13859             m[i].commit();
13860         }
13861     },
13862
13863     /**
13864      * Cancel outstanding changes on all changed records.
13865      */
13866     rejectChanges : function(){
13867         var m = this.modified.slice(0);
13868         this.modified = [];
13869         for(var i = 0, len = m.length; i < len; i++){
13870             m[i].reject();
13871         }
13872     },
13873
13874     onMetaChange : function(meta, rtype, o){
13875         this.recordType = rtype;
13876         this.fields = rtype.prototype.fields;
13877         delete this.snapshot;
13878         this.sortInfo = meta.sortInfo || this.sortInfo;
13879         this.modified = [];
13880         this.fireEvent('metachange', this, this.reader.meta);
13881     },
13882     
13883     moveIndex : function(data, type)
13884     {
13885         var index = this.indexOf(data);
13886         
13887         var newIndex = index + type;
13888         
13889         this.remove(data);
13890         
13891         this.insert(newIndex, data);
13892         
13893     }
13894 });/*
13895  * Based on:
13896  * Ext JS Library 1.1.1
13897  * Copyright(c) 2006-2007, Ext JS, LLC.
13898  *
13899  * Originally Released Under LGPL - original licence link has changed is not relivant.
13900  *
13901  * Fork - LGPL
13902  * <script type="text/javascript">
13903  */
13904
13905 /**
13906  * @class Roo.data.SimpleStore
13907  * @extends Roo.data.Store
13908  * Small helper class to make creating Stores from Array data easier.
13909  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13910  * @cfg {Array} fields An array of field definition objects, or field name strings.
13911  * @cfg {Object} an existing reader (eg. copied from another store)
13912  * @cfg {Array} data The multi-dimensional array of data
13913  * @constructor
13914  * @param {Object} config
13915  */
13916 Roo.data.SimpleStore = function(config)
13917 {
13918     Roo.data.SimpleStore.superclass.constructor.call(this, {
13919         isLocal : true,
13920         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13921                 id: config.id
13922             },
13923             Roo.data.Record.create(config.fields)
13924         ),
13925         proxy : new Roo.data.MemoryProxy(config.data)
13926     });
13927     this.load();
13928 };
13929 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13930  * Based on:
13931  * Ext JS Library 1.1.1
13932  * Copyright(c) 2006-2007, Ext JS, LLC.
13933  *
13934  * Originally Released Under LGPL - original licence link has changed is not relivant.
13935  *
13936  * Fork - LGPL
13937  * <script type="text/javascript">
13938  */
13939
13940 /**
13941 /**
13942  * @extends Roo.data.Store
13943  * @class Roo.data.JsonStore
13944  * Small helper class to make creating Stores for JSON data easier. <br/>
13945 <pre><code>
13946 var store = new Roo.data.JsonStore({
13947     url: 'get-images.php',
13948     root: 'images',
13949     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13950 });
13951 </code></pre>
13952  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13953  * JsonReader and HttpProxy (unless inline data is provided).</b>
13954  * @cfg {Array} fields An array of field definition objects, or field name strings.
13955  * @constructor
13956  * @param {Object} config
13957  */
13958 Roo.data.JsonStore = function(c){
13959     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13960         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13961         reader: new Roo.data.JsonReader(c, c.fields)
13962     }));
13963 };
13964 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13965  * Based on:
13966  * Ext JS Library 1.1.1
13967  * Copyright(c) 2006-2007, Ext JS, LLC.
13968  *
13969  * Originally Released Under LGPL - original licence link has changed is not relivant.
13970  *
13971  * Fork - LGPL
13972  * <script type="text/javascript">
13973  */
13974
13975  
13976 Roo.data.Field = function(config){
13977     if(typeof config == "string"){
13978         config = {name: config};
13979     }
13980     Roo.apply(this, config);
13981     
13982     if(!this.type){
13983         this.type = "auto";
13984     }
13985     
13986     var st = Roo.data.SortTypes;
13987     // named sortTypes are supported, here we look them up
13988     if(typeof this.sortType == "string"){
13989         this.sortType = st[this.sortType];
13990     }
13991     
13992     // set default sortType for strings and dates
13993     if(!this.sortType){
13994         switch(this.type){
13995             case "string":
13996                 this.sortType = st.asUCString;
13997                 break;
13998             case "date":
13999                 this.sortType = st.asDate;
14000                 break;
14001             default:
14002                 this.sortType = st.none;
14003         }
14004     }
14005
14006     // define once
14007     var stripRe = /[\$,%]/g;
14008
14009     // prebuilt conversion function for this field, instead of
14010     // switching every time we're reading a value
14011     if(!this.convert){
14012         var cv, dateFormat = this.dateFormat;
14013         switch(this.type){
14014             case "":
14015             case "auto":
14016             case undefined:
14017                 cv = function(v){ return v; };
14018                 break;
14019             case "string":
14020                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14021                 break;
14022             case "int":
14023                 cv = function(v){
14024                     return v !== undefined && v !== null && v !== '' ?
14025                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14026                     };
14027                 break;
14028             case "float":
14029                 cv = function(v){
14030                     return v !== undefined && v !== null && v !== '' ?
14031                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14032                     };
14033                 break;
14034             case "bool":
14035             case "boolean":
14036                 cv = function(v){ return v === true || v === "true" || v == 1; };
14037                 break;
14038             case "date":
14039                 cv = function(v){
14040                     if(!v){
14041                         return '';
14042                     }
14043                     if(v instanceof Date){
14044                         return v;
14045                     }
14046                     if(dateFormat){
14047                         if(dateFormat == "timestamp"){
14048                             return new Date(v*1000);
14049                         }
14050                         return Date.parseDate(v, dateFormat);
14051                     }
14052                     var parsed = Date.parse(v);
14053                     return parsed ? new Date(parsed) : null;
14054                 };
14055              break;
14056             
14057         }
14058         this.convert = cv;
14059     }
14060 };
14061
14062 Roo.data.Field.prototype = {
14063     dateFormat: null,
14064     defaultValue: "",
14065     mapping: null,
14066     sortType : null,
14067     sortDir : "ASC"
14068 };/*
14069  * Based on:
14070  * Ext JS Library 1.1.1
14071  * Copyright(c) 2006-2007, Ext JS, LLC.
14072  *
14073  * Originally Released Under LGPL - original licence link has changed is not relivant.
14074  *
14075  * Fork - LGPL
14076  * <script type="text/javascript">
14077  */
14078  
14079 // Base class for reading structured data from a data source.  This class is intended to be
14080 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14081
14082 /**
14083  * @class Roo.data.DataReader
14084  * Base class for reading structured data from a data source.  This class is intended to be
14085  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14086  */
14087
14088 Roo.data.DataReader = function(meta, recordType){
14089     
14090     this.meta = meta;
14091     
14092     this.recordType = recordType instanceof Array ? 
14093         Roo.data.Record.create(recordType) : recordType;
14094 };
14095
14096 Roo.data.DataReader.prototype = {
14097     
14098     
14099     readerType : 'Data',
14100      /**
14101      * Create an empty record
14102      * @param {Object} data (optional) - overlay some values
14103      * @return {Roo.data.Record} record created.
14104      */
14105     newRow :  function(d) {
14106         var da =  {};
14107         this.recordType.prototype.fields.each(function(c) {
14108             switch( c.type) {
14109                 case 'int' : da[c.name] = 0; break;
14110                 case 'date' : da[c.name] = new Date(); break;
14111                 case 'float' : da[c.name] = 0.0; break;
14112                 case 'boolean' : da[c.name] = false; break;
14113                 default : da[c.name] = ""; break;
14114             }
14115             
14116         });
14117         return new this.recordType(Roo.apply(da, d));
14118     }
14119     
14120     
14121 };/*
14122  * Based on:
14123  * Ext JS Library 1.1.1
14124  * Copyright(c) 2006-2007, Ext JS, LLC.
14125  *
14126  * Originally Released Under LGPL - original licence link has changed is not relivant.
14127  *
14128  * Fork - LGPL
14129  * <script type="text/javascript">
14130  */
14131
14132 /**
14133  * @class Roo.data.DataProxy
14134  * @extends Roo.data.Observable
14135  * This class is an abstract base class for implementations which provide retrieval of
14136  * unformatted data objects.<br>
14137  * <p>
14138  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14139  * (of the appropriate type which knows how to parse the data object) to provide a block of
14140  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14141  * <p>
14142  * Custom implementations must implement the load method as described in
14143  * {@link Roo.data.HttpProxy#load}.
14144  */
14145 Roo.data.DataProxy = function(){
14146     this.addEvents({
14147         /**
14148          * @event beforeload
14149          * Fires before a network request is made to retrieve a data object.
14150          * @param {Object} This DataProxy object.
14151          * @param {Object} params The params parameter to the load function.
14152          */
14153         beforeload : true,
14154         /**
14155          * @event load
14156          * Fires before the load method's callback is called.
14157          * @param {Object} This DataProxy object.
14158          * @param {Object} o The data object.
14159          * @param {Object} arg The callback argument object passed to the load function.
14160          */
14161         load : true,
14162         /**
14163          * @event loadexception
14164          * Fires if an Exception occurs during data retrieval.
14165          * @param {Object} This DataProxy object.
14166          * @param {Object} o The data object.
14167          * @param {Object} arg The callback argument object passed to the load function.
14168          * @param {Object} e The Exception.
14169          */
14170         loadexception : true
14171     });
14172     Roo.data.DataProxy.superclass.constructor.call(this);
14173 };
14174
14175 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14176
14177     /**
14178      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14179      */
14180 /*
14181  * Based on:
14182  * Ext JS Library 1.1.1
14183  * Copyright(c) 2006-2007, Ext JS, LLC.
14184  *
14185  * Originally Released Under LGPL - original licence link has changed is not relivant.
14186  *
14187  * Fork - LGPL
14188  * <script type="text/javascript">
14189  */
14190 /**
14191  * @class Roo.data.MemoryProxy
14192  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14193  * to the Reader when its load method is called.
14194  * @constructor
14195  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14196  */
14197 Roo.data.MemoryProxy = function(data){
14198     if (data.data) {
14199         data = data.data;
14200     }
14201     Roo.data.MemoryProxy.superclass.constructor.call(this);
14202     this.data = data;
14203 };
14204
14205 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14206     
14207     /**
14208      * Load data from the requested source (in this case an in-memory
14209      * data object passed to the constructor), read the data object into
14210      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14211      * process that block using the passed callback.
14212      * @param {Object} params This parameter is not used by the MemoryProxy class.
14213      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14214      * object into a block of Roo.data.Records.
14215      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14216      * The function must be passed <ul>
14217      * <li>The Record block object</li>
14218      * <li>The "arg" argument from the load function</li>
14219      * <li>A boolean success indicator</li>
14220      * </ul>
14221      * @param {Object} scope The scope in which to call the callback
14222      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14223      */
14224     load : function(params, reader, callback, scope, arg){
14225         params = params || {};
14226         var result;
14227         try {
14228             result = reader.readRecords(params.data ? params.data :this.data);
14229         }catch(e){
14230             this.fireEvent("loadexception", this, arg, null, e);
14231             callback.call(scope, null, arg, false);
14232             return;
14233         }
14234         callback.call(scope, result, arg, true);
14235     },
14236     
14237     // private
14238     update : function(params, records){
14239         
14240     }
14241 });/*
14242  * Based on:
14243  * Ext JS Library 1.1.1
14244  * Copyright(c) 2006-2007, Ext JS, LLC.
14245  *
14246  * Originally Released Under LGPL - original licence link has changed is not relivant.
14247  *
14248  * Fork - LGPL
14249  * <script type="text/javascript">
14250  */
14251 /**
14252  * @class Roo.data.HttpProxy
14253  * @extends Roo.data.DataProxy
14254  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14255  * configured to reference a certain URL.<br><br>
14256  * <p>
14257  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14258  * from which the running page was served.<br><br>
14259  * <p>
14260  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14261  * <p>
14262  * Be aware that to enable the browser to parse an XML document, the server must set
14263  * the Content-Type header in the HTTP response to "text/xml".
14264  * @constructor
14265  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14266  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14267  * will be used to make the request.
14268  */
14269 Roo.data.HttpProxy = function(conn){
14270     Roo.data.HttpProxy.superclass.constructor.call(this);
14271     // is conn a conn config or a real conn?
14272     this.conn = conn;
14273     this.useAjax = !conn || !conn.events;
14274   
14275 };
14276
14277 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14278     // thse are take from connection...
14279     
14280     /**
14281      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14282      */
14283     /**
14284      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14285      * extra parameters to each request made by this object. (defaults to undefined)
14286      */
14287     /**
14288      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14289      *  to each request made by this object. (defaults to undefined)
14290      */
14291     /**
14292      * @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)
14293      */
14294     /**
14295      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14296      */
14297      /**
14298      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14299      * @type Boolean
14300      */
14301   
14302
14303     /**
14304      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14305      * @type Boolean
14306      */
14307     /**
14308      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14309      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14310      * a finer-grained basis than the DataProxy events.
14311      */
14312     getConnection : function(){
14313         return this.useAjax ? Roo.Ajax : this.conn;
14314     },
14315
14316     /**
14317      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14318      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14319      * process that block using the passed callback.
14320      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14321      * for the request to the remote server.
14322      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14323      * object into a block of Roo.data.Records.
14324      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14325      * The function must be passed <ul>
14326      * <li>The Record block object</li>
14327      * <li>The "arg" argument from the load function</li>
14328      * <li>A boolean success indicator</li>
14329      * </ul>
14330      * @param {Object} scope The scope in which to call the callback
14331      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14332      */
14333     load : function(params, reader, callback, scope, arg){
14334         if(this.fireEvent("beforeload", this, params) !== false){
14335             var  o = {
14336                 params : params || {},
14337                 request: {
14338                     callback : callback,
14339                     scope : scope,
14340                     arg : arg
14341                 },
14342                 reader: reader,
14343                 callback : this.loadResponse,
14344                 scope: this
14345             };
14346             if(this.useAjax){
14347                 Roo.applyIf(o, this.conn);
14348                 if(this.activeRequest){
14349                     Roo.Ajax.abort(this.activeRequest);
14350                 }
14351                 this.activeRequest = Roo.Ajax.request(o);
14352             }else{
14353                 this.conn.request(o);
14354             }
14355         }else{
14356             callback.call(scope||this, null, arg, false);
14357         }
14358     },
14359
14360     // private
14361     loadResponse : function(o, success, response){
14362         delete this.activeRequest;
14363         if(!success){
14364             this.fireEvent("loadexception", this, o, response);
14365             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14366             return;
14367         }
14368         var result;
14369         try {
14370             result = o.reader.read(response);
14371         }catch(e){
14372             this.fireEvent("loadexception", this, o, response, e);
14373             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14374             return;
14375         }
14376         
14377         this.fireEvent("load", this, o, o.request.arg);
14378         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14379     },
14380
14381     // private
14382     update : function(dataSet){
14383
14384     },
14385
14386     // private
14387     updateResponse : function(dataSet){
14388
14389     }
14390 });/*
14391  * Based on:
14392  * Ext JS Library 1.1.1
14393  * Copyright(c) 2006-2007, Ext JS, LLC.
14394  *
14395  * Originally Released Under LGPL - original licence link has changed is not relivant.
14396  *
14397  * Fork - LGPL
14398  * <script type="text/javascript">
14399  */
14400
14401 /**
14402  * @class Roo.data.ScriptTagProxy
14403  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14404  * other than the originating domain of the running page.<br><br>
14405  * <p>
14406  * <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
14407  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14408  * <p>
14409  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14410  * source code that is used as the source inside a &lt;script> tag.<br><br>
14411  * <p>
14412  * In order for the browser to process the returned data, the server must wrap the data object
14413  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14414  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14415  * depending on whether the callback name was passed:
14416  * <p>
14417  * <pre><code>
14418 boolean scriptTag = false;
14419 String cb = request.getParameter("callback");
14420 if (cb != null) {
14421     scriptTag = true;
14422     response.setContentType("text/javascript");
14423 } else {
14424     response.setContentType("application/x-json");
14425 }
14426 Writer out = response.getWriter();
14427 if (scriptTag) {
14428     out.write(cb + "(");
14429 }
14430 out.print(dataBlock.toJsonString());
14431 if (scriptTag) {
14432     out.write(");");
14433 }
14434 </pre></code>
14435  *
14436  * @constructor
14437  * @param {Object} config A configuration object.
14438  */
14439 Roo.data.ScriptTagProxy = function(config){
14440     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14441     Roo.apply(this, config);
14442     this.head = document.getElementsByTagName("head")[0];
14443 };
14444
14445 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14446
14447 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14448     /**
14449      * @cfg {String} url The URL from which to request the data object.
14450      */
14451     /**
14452      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14453      */
14454     timeout : 30000,
14455     /**
14456      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14457      * the server the name of the callback function set up by the load call to process the returned data object.
14458      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14459      * javascript output which calls this named function passing the data object as its only parameter.
14460      */
14461     callbackParam : "callback",
14462     /**
14463      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14464      * name to the request.
14465      */
14466     nocache : true,
14467
14468     /**
14469      * Load data from the configured URL, read the data object into
14470      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14471      * process that block using the passed callback.
14472      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14473      * for the request to the remote server.
14474      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14475      * object into a block of Roo.data.Records.
14476      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14477      * The function must be passed <ul>
14478      * <li>The Record block object</li>
14479      * <li>The "arg" argument from the load function</li>
14480      * <li>A boolean success indicator</li>
14481      * </ul>
14482      * @param {Object} scope The scope in which to call the callback
14483      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14484      */
14485     load : function(params, reader, callback, scope, arg){
14486         if(this.fireEvent("beforeload", this, params) !== false){
14487
14488             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14489
14490             var url = this.url;
14491             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14492             if(this.nocache){
14493                 url += "&_dc=" + (new Date().getTime());
14494             }
14495             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14496             var trans = {
14497                 id : transId,
14498                 cb : "stcCallback"+transId,
14499                 scriptId : "stcScript"+transId,
14500                 params : params,
14501                 arg : arg,
14502                 url : url,
14503                 callback : callback,
14504                 scope : scope,
14505                 reader : reader
14506             };
14507             var conn = this;
14508
14509             window[trans.cb] = function(o){
14510                 conn.handleResponse(o, trans);
14511             };
14512
14513             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14514
14515             if(this.autoAbort !== false){
14516                 this.abort();
14517             }
14518
14519             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14520
14521             var script = document.createElement("script");
14522             script.setAttribute("src", url);
14523             script.setAttribute("type", "text/javascript");
14524             script.setAttribute("id", trans.scriptId);
14525             this.head.appendChild(script);
14526
14527             this.trans = trans;
14528         }else{
14529             callback.call(scope||this, null, arg, false);
14530         }
14531     },
14532
14533     // private
14534     isLoading : function(){
14535         return this.trans ? true : false;
14536     },
14537
14538     /**
14539      * Abort the current server request.
14540      */
14541     abort : function(){
14542         if(this.isLoading()){
14543             this.destroyTrans(this.trans);
14544         }
14545     },
14546
14547     // private
14548     destroyTrans : function(trans, isLoaded){
14549         this.head.removeChild(document.getElementById(trans.scriptId));
14550         clearTimeout(trans.timeoutId);
14551         if(isLoaded){
14552             window[trans.cb] = undefined;
14553             try{
14554                 delete window[trans.cb];
14555             }catch(e){}
14556         }else{
14557             // if hasn't been loaded, wait for load to remove it to prevent script error
14558             window[trans.cb] = function(){
14559                 window[trans.cb] = undefined;
14560                 try{
14561                     delete window[trans.cb];
14562                 }catch(e){}
14563             };
14564         }
14565     },
14566
14567     // private
14568     handleResponse : function(o, trans){
14569         this.trans = false;
14570         this.destroyTrans(trans, true);
14571         var result;
14572         try {
14573             result = trans.reader.readRecords(o);
14574         }catch(e){
14575             this.fireEvent("loadexception", this, o, trans.arg, e);
14576             trans.callback.call(trans.scope||window, null, trans.arg, false);
14577             return;
14578         }
14579         this.fireEvent("load", this, o, trans.arg);
14580         trans.callback.call(trans.scope||window, result, trans.arg, true);
14581     },
14582
14583     // private
14584     handleFailure : function(trans){
14585         this.trans = false;
14586         this.destroyTrans(trans, false);
14587         this.fireEvent("loadexception", this, null, trans.arg);
14588         trans.callback.call(trans.scope||window, null, trans.arg, false);
14589     }
14590 });/*
14591  * Based on:
14592  * Ext JS Library 1.1.1
14593  * Copyright(c) 2006-2007, Ext JS, LLC.
14594  *
14595  * Originally Released Under LGPL - original licence link has changed is not relivant.
14596  *
14597  * Fork - LGPL
14598  * <script type="text/javascript">
14599  */
14600
14601 /**
14602  * @class Roo.data.JsonReader
14603  * @extends Roo.data.DataReader
14604  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14605  * based on mappings in a provided Roo.data.Record constructor.
14606  * 
14607  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14608  * in the reply previously. 
14609  * 
14610  * <p>
14611  * Example code:
14612  * <pre><code>
14613 var RecordDef = Roo.data.Record.create([
14614     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14615     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14616 ]);
14617 var myReader = new Roo.data.JsonReader({
14618     totalProperty: "results",    // The property which contains the total dataset size (optional)
14619     root: "rows",                // The property which contains an Array of row objects
14620     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14621 }, RecordDef);
14622 </code></pre>
14623  * <p>
14624  * This would consume a JSON file like this:
14625  * <pre><code>
14626 { 'results': 2, 'rows': [
14627     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14628     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14629 }
14630 </code></pre>
14631  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14632  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14633  * paged from the remote server.
14634  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14635  * @cfg {String} root name of the property which contains the Array of row objects.
14636  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14637  * @cfg {Array} fields Array of field definition objects
14638  * @constructor
14639  * Create a new JsonReader
14640  * @param {Object} meta Metadata configuration options
14641  * @param {Object} recordType Either an Array of field definition objects,
14642  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14643  */
14644 Roo.data.JsonReader = function(meta, recordType){
14645     
14646     meta = meta || {};
14647     // set some defaults:
14648     Roo.applyIf(meta, {
14649         totalProperty: 'total',
14650         successProperty : 'success',
14651         root : 'data',
14652         id : 'id'
14653     });
14654     
14655     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14656 };
14657 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14658     
14659     readerType : 'Json',
14660     
14661     /**
14662      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14663      * Used by Store query builder to append _requestMeta to params.
14664      * 
14665      */
14666     metaFromRemote : false,
14667     /**
14668      * This method is only used by a DataProxy which has retrieved data from a remote server.
14669      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14670      * @return {Object} data A data block which is used by an Roo.data.Store object as
14671      * a cache of Roo.data.Records.
14672      */
14673     read : function(response){
14674         var json = response.responseText;
14675        
14676         var o = /* eval:var:o */ eval("("+json+")");
14677         if(!o) {
14678             throw {message: "JsonReader.read: Json object not found"};
14679         }
14680         
14681         if(o.metaData){
14682             
14683             delete this.ef;
14684             this.metaFromRemote = true;
14685             this.meta = o.metaData;
14686             this.recordType = Roo.data.Record.create(o.metaData.fields);
14687             this.onMetaChange(this.meta, this.recordType, o);
14688         }
14689         return this.readRecords(o);
14690     },
14691
14692     // private function a store will implement
14693     onMetaChange : function(meta, recordType, o){
14694
14695     },
14696
14697     /**
14698          * @ignore
14699          */
14700     simpleAccess: function(obj, subsc) {
14701         return obj[subsc];
14702     },
14703
14704         /**
14705          * @ignore
14706          */
14707     getJsonAccessor: function(){
14708         var re = /[\[\.]/;
14709         return function(expr) {
14710             try {
14711                 return(re.test(expr))
14712                     ? new Function("obj", "return obj." + expr)
14713                     : function(obj){
14714                         return obj[expr];
14715                     };
14716             } catch(e){}
14717             return Roo.emptyFn;
14718         };
14719     }(),
14720
14721     /**
14722      * Create a data block containing Roo.data.Records from an XML document.
14723      * @param {Object} o An object which contains an Array of row objects in the property specified
14724      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14725      * which contains the total size of the dataset.
14726      * @return {Object} data A data block which is used by an Roo.data.Store object as
14727      * a cache of Roo.data.Records.
14728      */
14729     readRecords : function(o){
14730         /**
14731          * After any data loads, the raw JSON data is available for further custom processing.
14732          * @type Object
14733          */
14734         this.o = o;
14735         var s = this.meta, Record = this.recordType,
14736             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14737
14738 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
14739         if (!this.ef) {
14740             if(s.totalProperty) {
14741                     this.getTotal = this.getJsonAccessor(s.totalProperty);
14742                 }
14743                 if(s.successProperty) {
14744                     this.getSuccess = this.getJsonAccessor(s.successProperty);
14745                 }
14746                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14747                 if (s.id) {
14748                         var g = this.getJsonAccessor(s.id);
14749                         this.getId = function(rec) {
14750                                 var r = g(rec);  
14751                                 return (r === undefined || r === "") ? null : r;
14752                         };
14753                 } else {
14754                         this.getId = function(){return null;};
14755                 }
14756             this.ef = [];
14757             for(var jj = 0; jj < fl; jj++){
14758                 f = fi[jj];
14759                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14760                 this.ef[jj] = this.getJsonAccessor(map);
14761             }
14762         }
14763
14764         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14765         if(s.totalProperty){
14766             var vt = parseInt(this.getTotal(o), 10);
14767             if(!isNaN(vt)){
14768                 totalRecords = vt;
14769             }
14770         }
14771         if(s.successProperty){
14772             var vs = this.getSuccess(o);
14773             if(vs === false || vs === 'false'){
14774                 success = false;
14775             }
14776         }
14777         var records = [];
14778         for(var i = 0; i < c; i++){
14779                 var n = root[i];
14780             var values = {};
14781             var id = this.getId(n);
14782             for(var j = 0; j < fl; j++){
14783                 f = fi[j];
14784             var v = this.ef[j](n);
14785             if (!f.convert) {
14786                 Roo.log('missing convert for ' + f.name);
14787                 Roo.log(f);
14788                 continue;
14789             }
14790             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14791             }
14792             var record = new Record(values, id);
14793             record.json = n;
14794             records[i] = record;
14795         }
14796         return {
14797             raw : o,
14798             success : success,
14799             records : records,
14800             totalRecords : totalRecords
14801         };
14802     },
14803     // used when loading children.. @see loadDataFromChildren
14804     toLoadData: function(rec)
14805     {
14806         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14807         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14808         return { data : data, total : data.length };
14809         
14810     }
14811 });/*
14812  * Based on:
14813  * Ext JS Library 1.1.1
14814  * Copyright(c) 2006-2007, Ext JS, LLC.
14815  *
14816  * Originally Released Under LGPL - original licence link has changed is not relivant.
14817  *
14818  * Fork - LGPL
14819  * <script type="text/javascript">
14820  */
14821
14822 /**
14823  * @class Roo.data.ArrayReader
14824  * @extends Roo.data.DataReader
14825  * Data reader class to create an Array of Roo.data.Record objects from an Array.
14826  * Each element of that Array represents a row of data fields. The
14827  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14828  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14829  * <p>
14830  * Example code:.
14831  * <pre><code>
14832 var RecordDef = Roo.data.Record.create([
14833     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
14834     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
14835 ]);
14836 var myReader = new Roo.data.ArrayReader({
14837     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
14838 }, RecordDef);
14839 </code></pre>
14840  * <p>
14841  * This would consume an Array like this:
14842  * <pre><code>
14843 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14844   </code></pre>
14845  
14846  * @constructor
14847  * Create a new JsonReader
14848  * @param {Object} meta Metadata configuration options.
14849  * @param {Object|Array} recordType Either an Array of field definition objects
14850  * 
14851  * @cfg {Array} fields Array of field definition objects
14852  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14853  * as specified to {@link Roo.data.Record#create},
14854  * or an {@link Roo.data.Record} object
14855  *
14856  * 
14857  * created using {@link Roo.data.Record#create}.
14858  */
14859 Roo.data.ArrayReader = function(meta, recordType)
14860 {    
14861     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14862 };
14863
14864 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14865     
14866       /**
14867      * Create a data block containing Roo.data.Records from an XML document.
14868      * @param {Object} o An Array of row objects which represents the dataset.
14869      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14870      * a cache of Roo.data.Records.
14871      */
14872     readRecords : function(o)
14873     {
14874         var sid = this.meta ? this.meta.id : null;
14875         var recordType = this.recordType, fields = recordType.prototype.fields;
14876         var records = [];
14877         var root = o;
14878         for(var i = 0; i < root.length; i++){
14879                 var n = root[i];
14880             var values = {};
14881             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14882             for(var j = 0, jlen = fields.length; j < jlen; j++){
14883                 var f = fields.items[j];
14884                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14885                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14886                 v = f.convert(v);
14887                 values[f.name] = v;
14888             }
14889             var record = new recordType(values, id);
14890             record.json = n;
14891             records[records.length] = record;
14892         }
14893         return {
14894             records : records,
14895             totalRecords : records.length
14896         };
14897     },
14898     // used when loading children.. @see loadDataFromChildren
14899     toLoadData: function(rec)
14900     {
14901         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14902         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14903         
14904     }
14905     
14906     
14907 });/*
14908  * - LGPL
14909  * * 
14910  */
14911
14912 /**
14913  * @class Roo.bootstrap.ComboBox
14914  * @extends Roo.bootstrap.TriggerField
14915  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14916  * @cfg {Boolean} append (true|false) default false
14917  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14918  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14919  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14920  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14921  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14922  * @cfg {Boolean} animate default true
14923  * @cfg {Boolean} emptyResultText only for touch device
14924  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14925  * @cfg {String} emptyTitle default ''
14926  * @cfg {Number} width fixed with? experimental
14927  * @constructor
14928  * Create a new ComboBox.
14929  * @param {Object} config Configuration options
14930  */
14931 Roo.bootstrap.ComboBox = function(config){
14932     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14933     this.addEvents({
14934         /**
14935          * @event expand
14936          * Fires when the dropdown list is expanded
14937         * @param {Roo.bootstrap.ComboBox} combo This combo box
14938         */
14939         'expand' : true,
14940         /**
14941          * @event collapse
14942          * Fires when the dropdown list is collapsed
14943         * @param {Roo.bootstrap.ComboBox} combo This combo box
14944         */
14945         'collapse' : true,
14946         /**
14947          * @event beforeselect
14948          * Fires before a list item is selected. Return false to cancel the selection.
14949         * @param {Roo.bootstrap.ComboBox} combo This combo box
14950         * @param {Roo.data.Record} record The data record returned from the underlying store
14951         * @param {Number} index The index of the selected item in the dropdown list
14952         */
14953         'beforeselect' : true,
14954         /**
14955          * @event select
14956          * Fires when a list item is selected
14957         * @param {Roo.bootstrap.ComboBox} combo This combo box
14958         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14959         * @param {Number} index The index of the selected item in the dropdown list
14960         */
14961         'select' : true,
14962         /**
14963          * @event beforequery
14964          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14965          * The event object passed has these properties:
14966         * @param {Roo.bootstrap.ComboBox} combo This combo box
14967         * @param {String} query The query
14968         * @param {Boolean} forceAll true to force "all" query
14969         * @param {Boolean} cancel true to cancel the query
14970         * @param {Object} e The query event object
14971         */
14972         'beforequery': true,
14973          /**
14974          * @event add
14975          * Fires when the 'add' icon is pressed (add a listener to enable add button)
14976         * @param {Roo.bootstrap.ComboBox} combo This combo box
14977         */
14978         'add' : true,
14979         /**
14980          * @event edit
14981          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14982         * @param {Roo.bootstrap.ComboBox} combo This combo box
14983         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14984         */
14985         'edit' : true,
14986         /**
14987          * @event remove
14988          * Fires when the remove value from the combobox array
14989         * @param {Roo.bootstrap.ComboBox} combo This combo box
14990         */
14991         'remove' : true,
14992         /**
14993          * @event afterremove
14994          * Fires when the remove value from the combobox array
14995         * @param {Roo.bootstrap.ComboBox} combo This combo box
14996         */
14997         'afterremove' : true,
14998         /**
14999          * @event specialfilter
15000          * Fires when specialfilter
15001             * @param {Roo.bootstrap.ComboBox} combo This combo box
15002             */
15003         'specialfilter' : true,
15004         /**
15005          * @event tick
15006          * Fires when tick the element
15007             * @param {Roo.bootstrap.ComboBox} combo This combo box
15008             */
15009         'tick' : true,
15010         /**
15011          * @event touchviewdisplay
15012          * Fires when touch view require special display (default is using displayField)
15013             * @param {Roo.bootstrap.ComboBox} combo This combo box
15014             * @param {Object} cfg set html .
15015             */
15016         'touchviewdisplay' : true
15017         
15018     });
15019     
15020     this.item = [];
15021     this.tickItems = [];
15022     
15023     this.selectedIndex = -1;
15024     if(this.mode == 'local'){
15025         if(config.queryDelay === undefined){
15026             this.queryDelay = 10;
15027         }
15028         if(config.minChars === undefined){
15029             this.minChars = 0;
15030         }
15031     }
15032 };
15033
15034 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15035      
15036     /**
15037      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15038      * rendering into an Roo.Editor, defaults to false)
15039      */
15040     /**
15041      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15042      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15043      */
15044     /**
15045      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15046      */
15047     /**
15048      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15049      * the dropdown list (defaults to undefined, with no header element)
15050      */
15051
15052      /**
15053      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15054      */
15055      
15056      /**
15057      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15058      */
15059     listWidth: undefined,
15060     /**
15061      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15062      * mode = 'remote' or 'text' if mode = 'local')
15063      */
15064     displayField: undefined,
15065     
15066     /**
15067      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15068      * mode = 'remote' or 'value' if mode = 'local'). 
15069      * Note: use of a valueField requires the user make a selection
15070      * in order for a value to be mapped.
15071      */
15072     valueField: undefined,
15073     /**
15074      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15075      */
15076     modalTitle : '',
15077     
15078     /**
15079      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15080      * field's data value (defaults to the underlying DOM element's name)
15081      */
15082     hiddenName: undefined,
15083     /**
15084      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15085      */
15086     listClass: '',
15087     /**
15088      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15089      */
15090     selectedClass: 'active',
15091     
15092     /**
15093      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15094      */
15095     shadow:'sides',
15096     /**
15097      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15098      * anchor positions (defaults to 'tl-bl')
15099      */
15100     listAlign: 'tl-bl?',
15101     /**
15102      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15103      */
15104     maxHeight: 300,
15105     /**
15106      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15107      * query specified by the allQuery config option (defaults to 'query')
15108      */
15109     triggerAction: 'query',
15110     /**
15111      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15112      * (defaults to 4, does not apply if editable = false)
15113      */
15114     minChars : 4,
15115     /**
15116      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15117      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15118      */
15119     typeAhead: false,
15120     /**
15121      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15122      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15123      */
15124     queryDelay: 500,
15125     /**
15126      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15127      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15128      */
15129     pageSize: 0,
15130     /**
15131      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15132      * when editable = true (defaults to false)
15133      */
15134     selectOnFocus:false,
15135     /**
15136      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15137      */
15138     queryParam: 'query',
15139     /**
15140      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15141      * when mode = 'remote' (defaults to 'Loading...')
15142      */
15143     loadingText: 'Loading...',
15144     /**
15145      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15146      */
15147     resizable: false,
15148     /**
15149      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15150      */
15151     handleHeight : 8,
15152     /**
15153      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15154      * traditional select (defaults to true)
15155      */
15156     editable: true,
15157     /**
15158      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15159      */
15160     allQuery: '',
15161     /**
15162      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15163      */
15164     mode: 'remote',
15165     /**
15166      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15167      * listWidth has a higher value)
15168      */
15169     minListWidth : 70,
15170     /**
15171      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15172      * allow the user to set arbitrary text into the field (defaults to false)
15173      */
15174     forceSelection:false,
15175     /**
15176      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15177      * if typeAhead = true (defaults to 250)
15178      */
15179     typeAheadDelay : 250,
15180     /**
15181      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15182      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15183      */
15184     valueNotFoundText : undefined,
15185     /**
15186      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15187      */
15188     blockFocus : false,
15189     
15190     /**
15191      * @cfg {Boolean} disableClear Disable showing of clear button.
15192      */
15193     disableClear : false,
15194     /**
15195      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15196      */
15197     alwaysQuery : false,
15198     
15199     /**
15200      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15201      */
15202     multiple : false,
15203     
15204     /**
15205      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15206      */
15207     invalidClass : "has-warning",
15208     
15209     /**
15210      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15211      */
15212     validClass : "has-success",
15213     
15214     /**
15215      * @cfg {Boolean} specialFilter (true|false) special filter default false
15216      */
15217     specialFilter : false,
15218     
15219     /**
15220      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15221      */
15222     mobileTouchView : true,
15223     
15224     /**
15225      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15226      */
15227     useNativeIOS : false,
15228     
15229     /**
15230      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15231      */
15232     mobile_restrict_height : false,
15233     
15234     ios_options : false,
15235     
15236     //private
15237     addicon : false,
15238     editicon: false,
15239     
15240     page: 0,
15241     hasQuery: false,
15242     append: false,
15243     loadNext: false,
15244     autoFocus : true,
15245     tickable : false,
15246     btnPosition : 'right',
15247     triggerList : true,
15248     showToggleBtn : true,
15249     animate : true,
15250     emptyResultText: 'Empty',
15251     triggerText : 'Select',
15252     emptyTitle : '',
15253     width : false,
15254     
15255     // element that contains real text value.. (when hidden is used..)
15256     
15257     getAutoCreate : function()
15258     {   
15259         var cfg = false;
15260         //render
15261         /*
15262          * Render classic select for iso
15263          */
15264         
15265         if(Roo.isIOS && this.useNativeIOS){
15266             cfg = this.getAutoCreateNativeIOS();
15267             return cfg;
15268         }
15269         
15270         /*
15271          * Touch Devices
15272          */
15273         
15274         if(Roo.isTouch && this.mobileTouchView){
15275             cfg = this.getAutoCreateTouchView();
15276             return cfg;;
15277         }
15278         
15279         /*
15280          *  Normal ComboBox
15281          */
15282         if(!this.tickable){
15283             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15284             return cfg;
15285         }
15286         
15287         /*
15288          *  ComboBox with tickable selections
15289          */
15290              
15291         var align = this.labelAlign || this.parentLabelAlign();
15292         
15293         cfg = {
15294             cls : 'form-group roo-combobox-tickable' //input-group
15295         };
15296         
15297         var btn_text_select = '';
15298         var btn_text_done = '';
15299         var btn_text_cancel = '';
15300         
15301         if (this.btn_text_show) {
15302             btn_text_select = 'Select';
15303             btn_text_done = 'Done';
15304             btn_text_cancel = 'Cancel'; 
15305         }
15306         
15307         var buttons = {
15308             tag : 'div',
15309             cls : 'tickable-buttons',
15310             cn : [
15311                 {
15312                     tag : 'button',
15313                     type : 'button',
15314                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15315                     //html : this.triggerText
15316                     html: btn_text_select
15317                 },
15318                 {
15319                     tag : 'button',
15320                     type : 'button',
15321                     name : 'ok',
15322                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15323                     //html : 'Done'
15324                     html: btn_text_done
15325                 },
15326                 {
15327                     tag : 'button',
15328                     type : 'button',
15329                     name : 'cancel',
15330                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15331                     //html : 'Cancel'
15332                     html: btn_text_cancel
15333                 }
15334             ]
15335         };
15336         
15337         if(this.editable){
15338             buttons.cn.unshift({
15339                 tag: 'input',
15340                 cls: 'roo-select2-search-field-input'
15341             });
15342         }
15343         
15344         var _this = this;
15345         
15346         Roo.each(buttons.cn, function(c){
15347             if (_this.size) {
15348                 c.cls += ' btn-' + _this.size;
15349             }
15350
15351             if (_this.disabled) {
15352                 c.disabled = true;
15353             }
15354         });
15355         
15356         var box = {
15357             tag: 'div',
15358             style : 'display: contents',
15359             cn: [
15360                 {
15361                     tag: 'input',
15362                     type : 'hidden',
15363                     cls: 'form-hidden-field'
15364                 },
15365                 {
15366                     tag: 'ul',
15367                     cls: 'roo-select2-choices',
15368                     cn:[
15369                         {
15370                             tag: 'li',
15371                             cls: 'roo-select2-search-field',
15372                             cn: [
15373                                 buttons
15374                             ]
15375                         }
15376                     ]
15377                 }
15378             ]
15379         };
15380         
15381         var combobox = {
15382             cls: 'roo-select2-container input-group roo-select2-container-multi',
15383             cn: [
15384                 
15385                 box
15386 //                {
15387 //                    tag: 'ul',
15388 //                    cls: 'typeahead typeahead-long dropdown-menu',
15389 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15390 //                }
15391             ]
15392         };
15393         
15394         if(this.hasFeedback && !this.allowBlank){
15395             
15396             var feedback = {
15397                 tag: 'span',
15398                 cls: 'glyphicon form-control-feedback'
15399             };
15400
15401             combobox.cn.push(feedback);
15402         }
15403         
15404         
15405         
15406         var indicator = {
15407             tag : 'i',
15408             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15409             tooltip : 'This field is required'
15410         };
15411         if (Roo.bootstrap.version == 4) {
15412             indicator = {
15413                 tag : 'i',
15414                 style : 'display:none'
15415             };
15416         }
15417         if (align ==='left' && this.fieldLabel.length) {
15418             
15419             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15420             
15421             cfg.cn = [
15422                 indicator,
15423                 {
15424                     tag: 'label',
15425                     'for' :  id,
15426                     cls : 'control-label col-form-label',
15427                     html : this.fieldLabel
15428
15429                 },
15430                 {
15431                     cls : "", 
15432                     cn: [
15433                         combobox
15434                     ]
15435                 }
15436
15437             ];
15438             
15439             var labelCfg = cfg.cn[1];
15440             var contentCfg = cfg.cn[2];
15441             
15442
15443             if(this.indicatorpos == 'right'){
15444                 
15445                 cfg.cn = [
15446                     {
15447                         tag: 'label',
15448                         'for' :  id,
15449                         cls : 'control-label col-form-label',
15450                         cn : [
15451                             {
15452                                 tag : 'span',
15453                                 html : this.fieldLabel
15454                             },
15455                             indicator
15456                         ]
15457                     },
15458                     {
15459                         cls : "",
15460                         cn: [
15461                             combobox
15462                         ]
15463                     }
15464
15465                 ];
15466                 
15467                 
15468                 
15469                 labelCfg = cfg.cn[0];
15470                 contentCfg = cfg.cn[1];
15471             
15472             }
15473             
15474             if(this.labelWidth > 12){
15475                 labelCfg.style = "width: " + this.labelWidth + 'px';
15476             }
15477             if(this.width * 1 > 0){
15478                 contentCfg.style = "width: " + this.width + 'px';
15479             }
15480             if(this.labelWidth < 13 && this.labelmd == 0){
15481                 this.labelmd = this.labelWidth;
15482             }
15483             
15484             if(this.labellg > 0){
15485                 labelCfg.cls += ' col-lg-' + this.labellg;
15486                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15487             }
15488             
15489             if(this.labelmd > 0){
15490                 labelCfg.cls += ' col-md-' + this.labelmd;
15491                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15492             }
15493             
15494             if(this.labelsm > 0){
15495                 labelCfg.cls += ' col-sm-' + this.labelsm;
15496                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15497             }
15498             
15499             if(this.labelxs > 0){
15500                 labelCfg.cls += ' col-xs-' + this.labelxs;
15501                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15502             }
15503                 
15504                 
15505         } else if ( this.fieldLabel.length) {
15506 //                Roo.log(" label");
15507                  cfg.cn = [
15508                    indicator,
15509                     {
15510                         tag: 'label',
15511                         //cls : 'input-group-addon',
15512                         html : this.fieldLabel
15513                     },
15514                     combobox
15515                 ];
15516                 
15517                 if(this.indicatorpos == 'right'){
15518                     cfg.cn = [
15519                         {
15520                             tag: 'label',
15521                             //cls : 'input-group-addon',
15522                             html : this.fieldLabel
15523                         },
15524                         indicator,
15525                         combobox
15526                     ];
15527                     
15528                 }
15529
15530         } else {
15531             
15532 //                Roo.log(" no label && no align");
15533                 cfg = combobox
15534                      
15535                 
15536         }
15537          
15538         var settings=this;
15539         ['xs','sm','md','lg'].map(function(size){
15540             if (settings[size]) {
15541                 cfg.cls += ' col-' + size + '-' + settings[size];
15542             }
15543         });
15544         
15545         return cfg;
15546         
15547     },
15548     
15549     _initEventsCalled : false,
15550     
15551     // private
15552     initEvents: function()
15553     {   
15554         if (this._initEventsCalled) { // as we call render... prevent looping...
15555             return;
15556         }
15557         this._initEventsCalled = true;
15558         
15559         if (!this.store) {
15560             throw "can not find store for combo";
15561         }
15562         
15563         this.indicator = this.indicatorEl();
15564         
15565         this.store = Roo.factory(this.store, Roo.data);
15566         this.store.parent = this;
15567         
15568         // if we are building from html. then this element is so complex, that we can not really
15569         // use the rendered HTML.
15570         // so we have to trash and replace the previous code.
15571         if (Roo.XComponent.build_from_html) {
15572             // remove this element....
15573             var e = this.el.dom, k=0;
15574             while (e ) { e = e.previousSibling;  ++k;}
15575
15576             this.el.remove();
15577             
15578             this.el=false;
15579             this.rendered = false;
15580             
15581             this.render(this.parent().getChildContainer(true), k);
15582         }
15583         
15584         if(Roo.isIOS && this.useNativeIOS){
15585             this.initIOSView();
15586             return;
15587         }
15588         
15589         /*
15590          * Touch Devices
15591          */
15592         
15593         if(Roo.isTouch && this.mobileTouchView){
15594             this.initTouchView();
15595             return;
15596         }
15597         
15598         if(this.tickable){
15599             this.initTickableEvents();
15600             return;
15601         }
15602         
15603         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15604         
15605         if(this.hiddenName){
15606             
15607             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15608             
15609             this.hiddenField.dom.value =
15610                 this.hiddenValue !== undefined ? this.hiddenValue :
15611                 this.value !== undefined ? this.value : '';
15612
15613             // prevent input submission
15614             this.el.dom.removeAttribute('name');
15615             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15616              
15617              
15618         }
15619         //if(Roo.isGecko){
15620         //    this.el.dom.setAttribute('autocomplete', 'off');
15621         //}
15622         
15623         var cls = 'x-combo-list';
15624         
15625         //this.list = new Roo.Layer({
15626         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15627         //});
15628         
15629         var _this = this;
15630         
15631         (function(){
15632             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15633             _this.list.setWidth(lw);
15634         }).defer(100);
15635         
15636         this.list.on('mouseover', this.onViewOver, this);
15637         this.list.on('mousemove', this.onViewMove, this);
15638         this.list.on('scroll', this.onViewScroll, this);
15639         
15640         /*
15641         this.list.swallowEvent('mousewheel');
15642         this.assetHeight = 0;
15643
15644         if(this.title){
15645             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15646             this.assetHeight += this.header.getHeight();
15647         }
15648
15649         this.innerList = this.list.createChild({cls:cls+'-inner'});
15650         this.innerList.on('mouseover', this.onViewOver, this);
15651         this.innerList.on('mousemove', this.onViewMove, this);
15652         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15653         
15654         if(this.allowBlank && !this.pageSize && !this.disableClear){
15655             this.footer = this.list.createChild({cls:cls+'-ft'});
15656             this.pageTb = new Roo.Toolbar(this.footer);
15657            
15658         }
15659         if(this.pageSize){
15660             this.footer = this.list.createChild({cls:cls+'-ft'});
15661             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15662                     {pageSize: this.pageSize});
15663             
15664         }
15665         
15666         if (this.pageTb && this.allowBlank && !this.disableClear) {
15667             var _this = this;
15668             this.pageTb.add(new Roo.Toolbar.Fill(), {
15669                 cls: 'x-btn-icon x-btn-clear',
15670                 text: '&#160;',
15671                 handler: function()
15672                 {
15673                     _this.collapse();
15674                     _this.clearValue();
15675                     _this.onSelect(false, -1);
15676                 }
15677             });
15678         }
15679         if (this.footer) {
15680             this.assetHeight += this.footer.getHeight();
15681         }
15682         */
15683             
15684         if(!this.tpl){
15685             this.tpl = Roo.bootstrap.version == 4 ?
15686                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15687                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15688         }
15689
15690         this.view = new Roo.View(this.list, this.tpl, {
15691             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15692         });
15693         //this.view.wrapEl.setDisplayed(false);
15694         this.view.on('click', this.onViewClick, this);
15695         
15696         
15697         this.store.on('beforeload', this.onBeforeLoad, this);
15698         this.store.on('load', this.onLoad, this);
15699         this.store.on('loadexception', this.onLoadException, this);
15700         /*
15701         if(this.resizable){
15702             this.resizer = new Roo.Resizable(this.list,  {
15703                pinned:true, handles:'se'
15704             });
15705             this.resizer.on('resize', function(r, w, h){
15706                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15707                 this.listWidth = w;
15708                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15709                 this.restrictHeight();
15710             }, this);
15711             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15712         }
15713         */
15714         if(!this.editable){
15715             this.editable = true;
15716             this.setEditable(false);
15717         }
15718         
15719         /*
15720         
15721         if (typeof(this.events.add.listeners) != 'undefined') {
15722             
15723             this.addicon = this.wrap.createChild(
15724                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
15725        
15726             this.addicon.on('click', function(e) {
15727                 this.fireEvent('add', this);
15728             }, this);
15729         }
15730         if (typeof(this.events.edit.listeners) != 'undefined') {
15731             
15732             this.editicon = this.wrap.createChild(
15733                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
15734             if (this.addicon) {
15735                 this.editicon.setStyle('margin-left', '40px');
15736             }
15737             this.editicon.on('click', function(e) {
15738                 
15739                 // we fire even  if inothing is selected..
15740                 this.fireEvent('edit', this, this.lastData );
15741                 
15742             }, this);
15743         }
15744         */
15745         
15746         this.keyNav = new Roo.KeyNav(this.inputEl(), {
15747             "up" : function(e){
15748                 this.inKeyMode = true;
15749                 this.selectPrev();
15750             },
15751
15752             "down" : function(e){
15753                 if(!this.isExpanded()){
15754                     this.onTriggerClick();
15755                 }else{
15756                     this.inKeyMode = true;
15757                     this.selectNext();
15758                 }
15759             },
15760
15761             "enter" : function(e){
15762 //                this.onViewClick();
15763                 //return true;
15764                 this.collapse();
15765                 
15766                 if(this.fireEvent("specialkey", this, e)){
15767                     this.onViewClick(false);
15768                 }
15769                 
15770                 return true;
15771             },
15772
15773             "esc" : function(e){
15774                 this.collapse();
15775             },
15776
15777             "tab" : function(e){
15778                 this.collapse();
15779                 
15780                 if(this.fireEvent("specialkey", this, e)){
15781                     this.onViewClick(false);
15782                 }
15783                 
15784                 return true;
15785             },
15786
15787             scope : this,
15788
15789             doRelay : function(foo, bar, hname){
15790                 if(hname == 'down' || this.scope.isExpanded()){
15791                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15792                 }
15793                 return true;
15794             },
15795
15796             forceKeyDown: true
15797         });
15798         
15799         
15800         this.queryDelay = Math.max(this.queryDelay || 10,
15801                 this.mode == 'local' ? 10 : 250);
15802         
15803         
15804         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15805         
15806         if(this.typeAhead){
15807             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15808         }
15809         if(this.editable !== false){
15810             this.inputEl().on("keyup", this.onKeyUp, this);
15811         }
15812         if(this.forceSelection){
15813             this.inputEl().on('blur', this.doForce, this);
15814         }
15815         
15816         if(this.multiple){
15817             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15818             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15819         }
15820     },
15821     
15822     initTickableEvents: function()
15823     {   
15824         this.createList();
15825         
15826         if(this.hiddenName){
15827             
15828             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15829             
15830             this.hiddenField.dom.value =
15831                 this.hiddenValue !== undefined ? this.hiddenValue :
15832                 this.value !== undefined ? this.value : '';
15833
15834             // prevent input submission
15835             this.el.dom.removeAttribute('name');
15836             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15837              
15838              
15839         }
15840         
15841 //        this.list = this.el.select('ul.dropdown-menu',true).first();
15842         
15843         this.choices = this.el.select('ul.roo-select2-choices', true).first();
15844         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15845         if(this.triggerList){
15846             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15847         }
15848          
15849         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15850         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15851         
15852         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15853         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15854         
15855         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15856         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15857         
15858         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15859         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15860         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15861         
15862         this.okBtn.hide();
15863         this.cancelBtn.hide();
15864         
15865         var _this = this;
15866         
15867         (function(){
15868             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15869             _this.list.setWidth(lw);
15870         }).defer(100);
15871         
15872         this.list.on('mouseover', this.onViewOver, this);
15873         this.list.on('mousemove', this.onViewMove, this);
15874         
15875         this.list.on('scroll', this.onViewScroll, this);
15876         
15877         if(!this.tpl){
15878             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
15879                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15880         }
15881
15882         this.view = new Roo.View(this.list, this.tpl, {
15883             singleSelect:true,
15884             tickable:true,
15885             parent:this,
15886             store: this.store,
15887             selectedClass: this.selectedClass
15888         });
15889         
15890         //this.view.wrapEl.setDisplayed(false);
15891         this.view.on('click', this.onViewClick, this);
15892         
15893         
15894         
15895         this.store.on('beforeload', this.onBeforeLoad, this);
15896         this.store.on('load', this.onLoad, this);
15897         this.store.on('loadexception', this.onLoadException, this);
15898         
15899         if(this.editable){
15900             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15901                 "up" : function(e){
15902                     this.inKeyMode = true;
15903                     this.selectPrev();
15904                 },
15905
15906                 "down" : function(e){
15907                     this.inKeyMode = true;
15908                     this.selectNext();
15909                 },
15910
15911                 "enter" : function(e){
15912                     if(this.fireEvent("specialkey", this, e)){
15913                         this.onViewClick(false);
15914                     }
15915                     
15916                     return true;
15917                 },
15918
15919                 "esc" : function(e){
15920                     this.onTickableFooterButtonClick(e, false, false);
15921                 },
15922
15923                 "tab" : function(e){
15924                     this.fireEvent("specialkey", this, e);
15925                     
15926                     this.onTickableFooterButtonClick(e, false, false);
15927                     
15928                     return true;
15929                 },
15930
15931                 scope : this,
15932
15933                 doRelay : function(e, fn, key){
15934                     if(this.scope.isExpanded()){
15935                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15936                     }
15937                     return true;
15938                 },
15939
15940                 forceKeyDown: true
15941             });
15942         }
15943         
15944         this.queryDelay = Math.max(this.queryDelay || 10,
15945                 this.mode == 'local' ? 10 : 250);
15946         
15947         
15948         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15949         
15950         if(this.typeAhead){
15951             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15952         }
15953         
15954         if(this.editable !== false){
15955             this.tickableInputEl().on("keyup", this.onKeyUp, this);
15956         }
15957         
15958         this.indicator = this.indicatorEl();
15959         
15960         if(this.indicator){
15961             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15962             this.indicator.hide();
15963         }
15964         
15965     },
15966
15967     onDestroy : function(){
15968         if(this.view){
15969             this.view.setStore(null);
15970             this.view.el.removeAllListeners();
15971             this.view.el.remove();
15972             this.view.purgeListeners();
15973         }
15974         if(this.list){
15975             this.list.dom.innerHTML  = '';
15976         }
15977         
15978         if(this.store){
15979             this.store.un('beforeload', this.onBeforeLoad, this);
15980             this.store.un('load', this.onLoad, this);
15981             this.store.un('loadexception', this.onLoadException, this);
15982         }
15983         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15984     },
15985
15986     // private
15987     fireKey : function(e){
15988         if(e.isNavKeyPress() && !this.list.isVisible()){
15989             this.fireEvent("specialkey", this, e);
15990         }
15991     },
15992
15993     // private
15994     onResize: function(w, h)
15995     {
15996         
15997         
15998 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15999 //        
16000 //        if(typeof w != 'number'){
16001 //            // we do not handle it!?!?
16002 //            return;
16003 //        }
16004 //        var tw = this.trigger.getWidth();
16005 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
16006 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
16007 //        var x = w - tw;
16008 //        this.inputEl().setWidth( this.adjustWidth('input', x));
16009 //            
16010 //        //this.trigger.setStyle('left', x+'px');
16011 //        
16012 //        if(this.list && this.listWidth === undefined){
16013 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16014 //            this.list.setWidth(lw);
16015 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16016 //        }
16017         
16018     
16019         
16020     },
16021
16022     /**
16023      * Allow or prevent the user from directly editing the field text.  If false is passed,
16024      * the user will only be able to select from the items defined in the dropdown list.  This method
16025      * is the runtime equivalent of setting the 'editable' config option at config time.
16026      * @param {Boolean} value True to allow the user to directly edit the field text
16027      */
16028     setEditable : function(value){
16029         if(value == this.editable){
16030             return;
16031         }
16032         this.editable = value;
16033         if(!value){
16034             this.inputEl().dom.setAttribute('readOnly', true);
16035             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16036             this.inputEl().addClass('x-combo-noedit');
16037         }else{
16038             this.inputEl().dom.setAttribute('readOnly', false);
16039             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16040             this.inputEl().removeClass('x-combo-noedit');
16041         }
16042     },
16043
16044     // private
16045     
16046     onBeforeLoad : function(combo,opts){
16047         if(!this.hasFocus){
16048             return;
16049         }
16050          if (!opts.add) {
16051             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16052          }
16053         this.restrictHeight();
16054         this.selectedIndex = -1;
16055     },
16056
16057     // private
16058     onLoad : function(){
16059         
16060         this.hasQuery = false;
16061         
16062         if(!this.hasFocus){
16063             return;
16064         }
16065         
16066         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16067             this.loading.hide();
16068         }
16069         
16070         if(this.store.getCount() > 0){
16071             
16072             this.expand();
16073             this.restrictHeight();
16074             if(this.lastQuery == this.allQuery){
16075                 if(this.editable && !this.tickable){
16076                     this.inputEl().dom.select();
16077                 }
16078                 
16079                 if(
16080                     !this.selectByValue(this.value, true) &&
16081                     this.autoFocus && 
16082                     (
16083                         !this.store.lastOptions ||
16084                         typeof(this.store.lastOptions.add) == 'undefined' || 
16085                         this.store.lastOptions.add != true
16086                     )
16087                 ){
16088                     this.select(0, true);
16089                 }
16090             }else{
16091                 if(this.autoFocus){
16092                     this.selectNext();
16093                 }
16094                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16095                     this.taTask.delay(this.typeAheadDelay);
16096                 }
16097             }
16098         }else{
16099             this.onEmptyResults();
16100         }
16101         
16102         //this.el.focus();
16103     },
16104     // private
16105     onLoadException : function()
16106     {
16107         this.hasQuery = false;
16108         
16109         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16110             this.loading.hide();
16111         }
16112         
16113         if(this.tickable && this.editable){
16114             return;
16115         }
16116         
16117         this.collapse();
16118         // only causes errors at present
16119         //Roo.log(this.store.reader.jsonData);
16120         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16121             // fixme
16122             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16123         //}
16124         
16125         
16126     },
16127     // private
16128     onTypeAhead : function(){
16129         if(this.store.getCount() > 0){
16130             var r = this.store.getAt(0);
16131             var newValue = r.data[this.displayField];
16132             var len = newValue.length;
16133             var selStart = this.getRawValue().length;
16134             
16135             if(selStart != len){
16136                 this.setRawValue(newValue);
16137                 this.selectText(selStart, newValue.length);
16138             }
16139         }
16140     },
16141
16142     // private
16143     onSelect : function(record, index){
16144         
16145         if(this.fireEvent('beforeselect', this, record, index) !== false){
16146         
16147             this.setFromData(index > -1 ? record.data : false);
16148             
16149             this.collapse();
16150             this.fireEvent('select', this, record, index);
16151         }
16152     },
16153
16154     /**
16155      * Returns the currently selected field value or empty string if no value is set.
16156      * @return {String} value The selected value
16157      */
16158     getValue : function()
16159     {
16160         if(Roo.isIOS && this.useNativeIOS){
16161             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16162         }
16163         
16164         if(this.multiple){
16165             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16166         }
16167         
16168         if(this.valueField){
16169             return typeof this.value != 'undefined' ? this.value : '';
16170         }else{
16171             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16172         }
16173     },
16174     
16175     getRawValue : function()
16176     {
16177         if(Roo.isIOS && this.useNativeIOS){
16178             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16179         }
16180         
16181         var v = this.inputEl().getValue();
16182         
16183         return v;
16184     },
16185
16186     /**
16187      * Clears any text/value currently set in the field
16188      */
16189     clearValue : function(){
16190         
16191         if(this.hiddenField){
16192             this.hiddenField.dom.value = '';
16193         }
16194         this.value = '';
16195         this.setRawValue('');
16196         this.lastSelectionText = '';
16197         this.lastData = false;
16198         
16199         var close = this.closeTriggerEl();
16200         
16201         if(close){
16202             close.hide();
16203         }
16204         
16205         this.validate();
16206         
16207     },
16208
16209     /**
16210      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16211      * will be displayed in the field.  If the value does not match the data value of an existing item,
16212      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16213      * Otherwise the field will be blank (although the value will still be set).
16214      * @param {String} value The value to match
16215      */
16216     setValue : function(v)
16217     {
16218         if(Roo.isIOS && this.useNativeIOS){
16219             this.setIOSValue(v);
16220             return;
16221         }
16222         
16223         if(this.multiple){
16224             this.syncValue();
16225             return;
16226         }
16227         
16228         var text = v;
16229         if(this.valueField){
16230             var r = this.findRecord(this.valueField, v);
16231             if(r){
16232                 text = r.data[this.displayField];
16233             }else if(this.valueNotFoundText !== undefined){
16234                 text = this.valueNotFoundText;
16235             }
16236         }
16237         this.lastSelectionText = text;
16238         if(this.hiddenField){
16239             this.hiddenField.dom.value = v;
16240         }
16241         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16242         this.value = v;
16243         
16244         var close = this.closeTriggerEl();
16245         
16246         if(close){
16247             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16248         }
16249         
16250         this.validate();
16251     },
16252     /**
16253      * @property {Object} the last set data for the element
16254      */
16255     
16256     lastData : false,
16257     /**
16258      * Sets the value of the field based on a object which is related to the record format for the store.
16259      * @param {Object} value the value to set as. or false on reset?
16260      */
16261     setFromData : function(o){
16262         
16263         if(this.multiple){
16264             this.addItem(o);
16265             return;
16266         }
16267             
16268         var dv = ''; // display value
16269         var vv = ''; // value value..
16270         this.lastData = o;
16271         if (this.displayField) {
16272             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16273         } else {
16274             // this is an error condition!!!
16275             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16276         }
16277         
16278         if(this.valueField){
16279             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16280         }
16281         
16282         var close = this.closeTriggerEl();
16283         
16284         if(close){
16285             if(dv.length || vv * 1 > 0){
16286                 close.show() ;
16287                 this.blockFocus=true;
16288             } else {
16289                 close.hide();
16290             }             
16291         }
16292         
16293         if(this.hiddenField){
16294             this.hiddenField.dom.value = vv;
16295             
16296             this.lastSelectionText = dv;
16297             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16298             this.value = vv;
16299             return;
16300         }
16301         // no hidden field.. - we store the value in 'value', but still display
16302         // display field!!!!
16303         this.lastSelectionText = dv;
16304         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16305         this.value = vv;
16306         
16307         
16308         
16309     },
16310     // private
16311     reset : function(){
16312         // overridden so that last data is reset..
16313         
16314         if(this.multiple){
16315             this.clearItem();
16316             return;
16317         }
16318         
16319         this.setValue(this.originalValue);
16320         //this.clearInvalid();
16321         this.lastData = false;
16322         if (this.view) {
16323             this.view.clearSelections();
16324         }
16325         
16326         this.validate();
16327     },
16328     // private
16329     findRecord : function(prop, value){
16330         var record;
16331         if(this.store.getCount() > 0){
16332             this.store.each(function(r){
16333                 if(r.data[prop] == value){
16334                     record = r;
16335                     return false;
16336                 }
16337                 return true;
16338             });
16339         }
16340         return record;
16341     },
16342     
16343     getName: function()
16344     {
16345         // returns hidden if it's set..
16346         if (!this.rendered) {return ''};
16347         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16348         
16349     },
16350     // private
16351     onViewMove : function(e, t){
16352         this.inKeyMode = false;
16353     },
16354
16355     // private
16356     onViewOver : function(e, t){
16357         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16358             return;
16359         }
16360         var item = this.view.findItemFromChild(t);
16361         
16362         if(item){
16363             var index = this.view.indexOf(item);
16364             this.select(index, false);
16365         }
16366     },
16367
16368     // private
16369     onViewClick : function(view, doFocus, el, e)
16370     {
16371         var index = this.view.getSelectedIndexes()[0];
16372         
16373         var r = this.store.getAt(index);
16374         
16375         if(this.tickable){
16376             
16377             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16378                 return;
16379             }
16380             
16381             var rm = false;
16382             var _this = this;
16383             
16384             Roo.each(this.tickItems, function(v,k){
16385                 
16386                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16387                     Roo.log(v);
16388                     _this.tickItems.splice(k, 1);
16389                     
16390                     if(typeof(e) == 'undefined' && view == false){
16391                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16392                     }
16393                     
16394                     rm = true;
16395                     return;
16396                 }
16397             });
16398             
16399             if(rm){
16400                 return;
16401             }
16402             
16403             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16404                 this.tickItems.push(r.data);
16405             }
16406             
16407             if(typeof(e) == 'undefined' && view == false){
16408                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16409             }
16410                     
16411             return;
16412         }
16413         
16414         if(r){
16415             this.onSelect(r, index);
16416         }
16417         if(doFocus !== false && !this.blockFocus){
16418             this.inputEl().focus();
16419         }
16420     },
16421
16422     // private
16423     restrictHeight : function(){
16424         //this.innerList.dom.style.height = '';
16425         //var inner = this.innerList.dom;
16426         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16427         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16428         //this.list.beginUpdate();
16429         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16430         this.list.alignTo(this.inputEl(), this.listAlign);
16431         this.list.alignTo(this.inputEl(), this.listAlign);
16432         //this.list.endUpdate();
16433     },
16434
16435     // private
16436     onEmptyResults : function(){
16437         
16438         if(this.tickable && this.editable){
16439             this.hasFocus = false;
16440             this.restrictHeight();
16441             return;
16442         }
16443         
16444         this.collapse();
16445     },
16446
16447     /**
16448      * Returns true if the dropdown list is expanded, else false.
16449      */
16450     isExpanded : function(){
16451         return this.list.isVisible();
16452     },
16453
16454     /**
16455      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16456      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16457      * @param {String} value The data value of the item to select
16458      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16459      * selected item if it is not currently in view (defaults to true)
16460      * @return {Boolean} True if the value matched an item in the list, else false
16461      */
16462     selectByValue : function(v, scrollIntoView){
16463         if(v !== undefined && v !== null){
16464             var r = this.findRecord(this.valueField || this.displayField, v);
16465             if(r){
16466                 this.select(this.store.indexOf(r), scrollIntoView);
16467                 return true;
16468             }
16469         }
16470         return false;
16471     },
16472
16473     /**
16474      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16475      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16476      * @param {Number} index The zero-based index of the list item to select
16477      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16478      * selected item if it is not currently in view (defaults to true)
16479      */
16480     select : function(index, scrollIntoView){
16481         this.selectedIndex = index;
16482         this.view.select(index);
16483         if(scrollIntoView !== false){
16484             var el = this.view.getNode(index);
16485             /*
16486              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16487              */
16488             if(el){
16489                 this.list.scrollChildIntoView(el, false);
16490             }
16491         }
16492     },
16493
16494     // private
16495     selectNext : function(){
16496         var ct = this.store.getCount();
16497         if(ct > 0){
16498             if(this.selectedIndex == -1){
16499                 this.select(0);
16500             }else if(this.selectedIndex < ct-1){
16501                 this.select(this.selectedIndex+1);
16502             }
16503         }
16504     },
16505
16506     // private
16507     selectPrev : function(){
16508         var ct = this.store.getCount();
16509         if(ct > 0){
16510             if(this.selectedIndex == -1){
16511                 this.select(0);
16512             }else if(this.selectedIndex != 0){
16513                 this.select(this.selectedIndex-1);
16514             }
16515         }
16516     },
16517
16518     // private
16519     onKeyUp : function(e){
16520         if(this.editable !== false && !e.isSpecialKey()){
16521             this.lastKey = e.getKey();
16522             this.dqTask.delay(this.queryDelay);
16523         }
16524     },
16525
16526     // private
16527     validateBlur : function(){
16528         return !this.list || !this.list.isVisible();   
16529     },
16530
16531     // private
16532     initQuery : function(){
16533         
16534         var v = this.getRawValue();
16535         
16536         if(this.tickable && this.editable){
16537             v = this.tickableInputEl().getValue();
16538         }
16539         
16540         this.doQuery(v);
16541     },
16542
16543     // private
16544     doForce : function(){
16545         if(this.inputEl().dom.value.length > 0){
16546             this.inputEl().dom.value =
16547                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16548              
16549         }
16550     },
16551
16552     /**
16553      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16554      * query allowing the query action to be canceled if needed.
16555      * @param {String} query The SQL query to execute
16556      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16557      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16558      * saved in the current store (defaults to false)
16559      */
16560     doQuery : function(q, forceAll){
16561         
16562         if(q === undefined || q === null){
16563             q = '';
16564         }
16565         var qe = {
16566             query: q,
16567             forceAll: forceAll,
16568             combo: this,
16569             cancel:false
16570         };
16571         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16572             return false;
16573         }
16574         q = qe.query;
16575         
16576         forceAll = qe.forceAll;
16577         if(forceAll === true || (q.length >= this.minChars)){
16578             
16579             this.hasQuery = true;
16580             
16581             if(this.lastQuery != q || this.alwaysQuery){
16582                 this.lastQuery = q;
16583                 if(this.mode == 'local'){
16584                     this.selectedIndex = -1;
16585                     if(forceAll){
16586                         this.store.clearFilter();
16587                     }else{
16588                         
16589                         if(this.specialFilter){
16590                             this.fireEvent('specialfilter', this);
16591                             this.onLoad();
16592                             return;
16593                         }
16594                         
16595                         this.store.filter(this.displayField, q);
16596                     }
16597                     
16598                     this.store.fireEvent("datachanged", this.store);
16599                     
16600                     this.onLoad();
16601                     
16602                     
16603                 }else{
16604                     
16605                     this.store.baseParams[this.queryParam] = q;
16606                     
16607                     var options = {params : this.getParams(q)};
16608                     
16609                     if(this.loadNext){
16610                         options.add = true;
16611                         options.params.start = this.page * this.pageSize;
16612                     }
16613                     
16614                     this.store.load(options);
16615                     
16616                     /*
16617                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16618                      *  we should expand the list on onLoad
16619                      *  so command out it
16620                      */
16621 //                    this.expand();
16622                 }
16623             }else{
16624                 this.selectedIndex = -1;
16625                 this.onLoad();   
16626             }
16627         }
16628         
16629         this.loadNext = false;
16630     },
16631     
16632     // private
16633     getParams : function(q){
16634         var p = {};
16635         //p[this.queryParam] = q;
16636         
16637         if(this.pageSize){
16638             p.start = 0;
16639             p.limit = this.pageSize;
16640         }
16641         return p;
16642     },
16643
16644     /**
16645      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16646      */
16647     collapse : function(){
16648         if(!this.isExpanded()){
16649             return;
16650         }
16651         
16652         this.list.hide();
16653         
16654         this.hasFocus = false;
16655         
16656         if(this.tickable){
16657             this.okBtn.hide();
16658             this.cancelBtn.hide();
16659             this.trigger.show();
16660             
16661             if(this.editable){
16662                 this.tickableInputEl().dom.value = '';
16663                 this.tickableInputEl().blur();
16664             }
16665             
16666         }
16667         
16668         Roo.get(document).un('mousedown', this.collapseIf, this);
16669         Roo.get(document).un('mousewheel', this.collapseIf, this);
16670         if (!this.editable) {
16671             Roo.get(document).un('keydown', this.listKeyPress, this);
16672         }
16673         this.fireEvent('collapse', this);
16674         
16675         this.validate();
16676     },
16677
16678     // private
16679     collapseIf : function(e){
16680         var in_combo  = e.within(this.el);
16681         var in_list =  e.within(this.list);
16682         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16683         
16684         if (in_combo || in_list || is_list) {
16685             //e.stopPropagation();
16686             return;
16687         }
16688         
16689         if(this.tickable){
16690             this.onTickableFooterButtonClick(e, false, false);
16691         }
16692
16693         this.collapse();
16694         
16695     },
16696
16697     /**
16698      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16699      */
16700     expand : function(){
16701        
16702         if(this.isExpanded() || !this.hasFocus){
16703             return;
16704         }
16705         
16706         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16707         this.list.setWidth(lw);
16708         
16709         Roo.log('expand');
16710         
16711         this.list.show();
16712         
16713         this.restrictHeight();
16714         
16715         if(this.tickable){
16716             
16717             this.tickItems = Roo.apply([], this.item);
16718             
16719             this.okBtn.show();
16720             this.cancelBtn.show();
16721             this.trigger.hide();
16722             
16723             if(this.editable){
16724                 this.tickableInputEl().focus();
16725             }
16726             
16727         }
16728         
16729         Roo.get(document).on('mousedown', this.collapseIf, this);
16730         Roo.get(document).on('mousewheel', this.collapseIf, this);
16731         if (!this.editable) {
16732             Roo.get(document).on('keydown', this.listKeyPress, this);
16733         }
16734         
16735         this.fireEvent('expand', this);
16736     },
16737
16738     // private
16739     // Implements the default empty TriggerField.onTriggerClick function
16740     onTriggerClick : function(e)
16741     {
16742         Roo.log('trigger click');
16743         
16744         if(this.disabled || !this.triggerList){
16745             return;
16746         }
16747         
16748         this.page = 0;
16749         this.loadNext = false;
16750         
16751         if(this.isExpanded()){
16752             this.collapse();
16753             if (!this.blockFocus) {
16754                 this.inputEl().focus();
16755             }
16756             
16757         }else {
16758             this.hasFocus = true;
16759             if(this.triggerAction == 'all') {
16760                 this.doQuery(this.allQuery, true);
16761             } else {
16762                 this.doQuery(this.getRawValue());
16763             }
16764             if (!this.blockFocus) {
16765                 this.inputEl().focus();
16766             }
16767         }
16768     },
16769     
16770     onTickableTriggerClick : function(e)
16771     {
16772         if(this.disabled){
16773             return;
16774         }
16775         
16776         this.page = 0;
16777         this.loadNext = false;
16778         this.hasFocus = true;
16779         
16780         if(this.triggerAction == 'all') {
16781             this.doQuery(this.allQuery, true);
16782         } else {
16783             this.doQuery(this.getRawValue());
16784         }
16785     },
16786     
16787     onSearchFieldClick : function(e)
16788     {
16789         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16790             this.onTickableFooterButtonClick(e, false, false);
16791             return;
16792         }
16793         
16794         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16795             return;
16796         }
16797         
16798         this.page = 0;
16799         this.loadNext = false;
16800         this.hasFocus = true;
16801         
16802         if(this.triggerAction == 'all') {
16803             this.doQuery(this.allQuery, true);
16804         } else {
16805             this.doQuery(this.getRawValue());
16806         }
16807     },
16808     
16809     listKeyPress : function(e)
16810     {
16811         //Roo.log('listkeypress');
16812         // scroll to first matching element based on key pres..
16813         if (e.isSpecialKey()) {
16814             return false;
16815         }
16816         var k = String.fromCharCode(e.getKey()).toUpperCase();
16817         //Roo.log(k);
16818         var match  = false;
16819         var csel = this.view.getSelectedNodes();
16820         var cselitem = false;
16821         if (csel.length) {
16822             var ix = this.view.indexOf(csel[0]);
16823             cselitem  = this.store.getAt(ix);
16824             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16825                 cselitem = false;
16826             }
16827             
16828         }
16829         
16830         this.store.each(function(v) { 
16831             if (cselitem) {
16832                 // start at existing selection.
16833                 if (cselitem.id == v.id) {
16834                     cselitem = false;
16835                 }
16836                 return true;
16837             }
16838                 
16839             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16840                 match = this.store.indexOf(v);
16841                 return false;
16842             }
16843             return true;
16844         }, this);
16845         
16846         if (match === false) {
16847             return true; // no more action?
16848         }
16849         // scroll to?
16850         this.view.select(match);
16851         var sn = Roo.get(this.view.getSelectedNodes()[0]);
16852         sn.scrollIntoView(sn.dom.parentNode, false);
16853     },
16854     
16855     onViewScroll : function(e, t){
16856         
16857         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){
16858             return;
16859         }
16860         
16861         this.hasQuery = true;
16862         
16863         this.loading = this.list.select('.loading', true).first();
16864         
16865         if(this.loading === null){
16866             this.list.createChild({
16867                 tag: 'div',
16868                 cls: 'loading roo-select2-more-results roo-select2-active',
16869                 html: 'Loading more results...'
16870             });
16871             
16872             this.loading = this.list.select('.loading', true).first();
16873             
16874             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16875             
16876             this.loading.hide();
16877         }
16878         
16879         this.loading.show();
16880         
16881         var _combo = this;
16882         
16883         this.page++;
16884         this.loadNext = true;
16885         
16886         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16887         
16888         return;
16889     },
16890     
16891     addItem : function(o)
16892     {   
16893         var dv = ''; // display value
16894         
16895         if (this.displayField) {
16896             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16897         } else {
16898             // this is an error condition!!!
16899             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16900         }
16901         
16902         if(!dv.length){
16903             return;
16904         }
16905         
16906         var choice = this.choices.createChild({
16907             tag: 'li',
16908             cls: 'roo-select2-search-choice',
16909             cn: [
16910                 {
16911                     tag: 'div',
16912                     html: dv
16913                 },
16914                 {
16915                     tag: 'a',
16916                     href: '#',
16917                     cls: 'roo-select2-search-choice-close fa fa-times',
16918                     tabindex: '-1'
16919                 }
16920             ]
16921             
16922         }, this.searchField);
16923         
16924         var close = choice.select('a.roo-select2-search-choice-close', true).first();
16925         
16926         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16927         
16928         this.item.push(o);
16929         
16930         this.lastData = o;
16931         
16932         this.syncValue();
16933         
16934         this.inputEl().dom.value = '';
16935         
16936         this.validate();
16937     },
16938     
16939     onRemoveItem : function(e, _self, o)
16940     {
16941         e.preventDefault();
16942         
16943         this.lastItem = Roo.apply([], this.item);
16944         
16945         var index = this.item.indexOf(o.data) * 1;
16946         
16947         if( index < 0){
16948             Roo.log('not this item?!');
16949             return;
16950         }
16951         
16952         this.item.splice(index, 1);
16953         o.item.remove();
16954         
16955         this.syncValue();
16956         
16957         this.fireEvent('remove', this, e);
16958         
16959         this.validate();
16960         
16961     },
16962     
16963     syncValue : function()
16964     {
16965         if(!this.item.length){
16966             this.clearValue();
16967             return;
16968         }
16969             
16970         var value = [];
16971         var _this = this;
16972         Roo.each(this.item, function(i){
16973             if(_this.valueField){
16974                 value.push(i[_this.valueField]);
16975                 return;
16976             }
16977
16978             value.push(i);
16979         });
16980
16981         this.value = value.join(',');
16982
16983         if(this.hiddenField){
16984             this.hiddenField.dom.value = this.value;
16985         }
16986         
16987         this.store.fireEvent("datachanged", this.store);
16988         
16989         this.validate();
16990     },
16991     
16992     clearItem : function()
16993     {
16994         if(!this.multiple){
16995             return;
16996         }
16997         
16998         this.item = [];
16999         
17000         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17001            c.remove();
17002         });
17003         
17004         this.syncValue();
17005         
17006         this.validate();
17007         
17008         if(this.tickable && !Roo.isTouch){
17009             this.view.refresh();
17010         }
17011     },
17012     
17013     inputEl: function ()
17014     {
17015         if(Roo.isIOS && this.useNativeIOS){
17016             return this.el.select('select.roo-ios-select', true).first();
17017         }
17018         
17019         if(Roo.isTouch && this.mobileTouchView){
17020             return this.el.select('input.form-control',true).first();
17021         }
17022         
17023         if(this.tickable){
17024             return this.searchField;
17025         }
17026         
17027         return this.el.select('input.form-control',true).first();
17028     },
17029     
17030     onTickableFooterButtonClick : function(e, btn, el)
17031     {
17032         e.preventDefault();
17033         
17034         this.lastItem = Roo.apply([], this.item);
17035         
17036         if(btn && btn.name == 'cancel'){
17037             this.tickItems = Roo.apply([], this.item);
17038             this.collapse();
17039             return;
17040         }
17041         
17042         this.clearItem();
17043         
17044         var _this = this;
17045         
17046         Roo.each(this.tickItems, function(o){
17047             _this.addItem(o);
17048         });
17049         
17050         this.collapse();
17051         
17052     },
17053     
17054     validate : function()
17055     {
17056         if(this.getVisibilityEl().hasClass('hidden')){
17057             return true;
17058         }
17059         
17060         var v = this.getRawValue();
17061         
17062         if(this.multiple){
17063             v = this.getValue();
17064         }
17065         
17066         if(this.disabled || this.allowBlank || v.length){
17067             this.markValid();
17068             return true;
17069         }
17070         
17071         this.markInvalid();
17072         return false;
17073     },
17074     
17075     tickableInputEl : function()
17076     {
17077         if(!this.tickable || !this.editable){
17078             return this.inputEl();
17079         }
17080         
17081         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17082     },
17083     
17084     
17085     getAutoCreateTouchView : function()
17086     {
17087         var id = Roo.id();
17088         
17089         var cfg = {
17090             cls: 'form-group' //input-group
17091         };
17092         
17093         var input =  {
17094             tag: 'input',
17095             id : id,
17096             type : this.inputType,
17097             cls : 'form-control x-combo-noedit',
17098             autocomplete: 'new-password',
17099             placeholder : this.placeholder || '',
17100             readonly : true
17101         };
17102         
17103         if (this.name) {
17104             input.name = this.name;
17105         }
17106         
17107         if (this.size) {
17108             input.cls += ' input-' + this.size;
17109         }
17110         
17111         if (this.disabled) {
17112             input.disabled = true;
17113         }
17114         
17115         var inputblock = {
17116             cls : 'roo-combobox-wrap',
17117             cn : [
17118                 input
17119             ]
17120         };
17121         
17122         if(this.before){
17123             inputblock.cls += ' input-group';
17124             
17125             inputblock.cn.unshift({
17126                 tag :'span',
17127                 cls : 'input-group-addon input-group-prepend input-group-text',
17128                 html : this.before
17129             });
17130         }
17131         
17132         if(this.removable && !this.multiple){
17133             inputblock.cls += ' roo-removable';
17134             
17135             inputblock.cn.push({
17136                 tag: 'button',
17137                 html : 'x',
17138                 cls : 'roo-combo-removable-btn close'
17139             });
17140         }
17141
17142         if(this.hasFeedback && !this.allowBlank){
17143             
17144             inputblock.cls += ' has-feedback';
17145             
17146             inputblock.cn.push({
17147                 tag: 'span',
17148                 cls: 'glyphicon form-control-feedback'
17149             });
17150             
17151         }
17152         
17153         if (this.after) {
17154             
17155             inputblock.cls += (this.before) ? '' : ' input-group';
17156             
17157             inputblock.cn.push({
17158                 tag :'span',
17159                 cls : 'input-group-addon input-group-append input-group-text',
17160                 html : this.after
17161             });
17162         }
17163
17164         
17165         var ibwrap = inputblock;
17166         
17167         if(this.multiple){
17168             ibwrap = {
17169                 tag: 'ul',
17170                 cls: 'roo-select2-choices',
17171                 cn:[
17172                     {
17173                         tag: 'li',
17174                         cls: 'roo-select2-search-field',
17175                         cn: [
17176
17177                             inputblock
17178                         ]
17179                     }
17180                 ]
17181             };
17182         
17183             
17184         }
17185         
17186         var combobox = {
17187             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17188             cn: [
17189                 {
17190                     tag: 'input',
17191                     type : 'hidden',
17192                     cls: 'form-hidden-field'
17193                 },
17194                 ibwrap
17195             ]
17196         };
17197         
17198         if(!this.multiple && this.showToggleBtn){
17199             
17200             var caret = {
17201                 cls: 'caret'
17202             };
17203             
17204             if (this.caret != false) {
17205                 caret = {
17206                      tag: 'i',
17207                      cls: 'fa fa-' + this.caret
17208                 };
17209                 
17210             }
17211             
17212             combobox.cn.push({
17213                 tag :'span',
17214                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17215                 cn : [
17216                     Roo.bootstrap.version == 3 ? caret : '',
17217                     {
17218                         tag: 'span',
17219                         cls: 'combobox-clear',
17220                         cn  : [
17221                             {
17222                                 tag : 'i',
17223                                 cls: 'icon-remove'
17224                             }
17225                         ]
17226                     }
17227                 ]
17228
17229             })
17230         }
17231         
17232         if(this.multiple){
17233             combobox.cls += ' roo-select2-container-multi';
17234         }
17235         
17236         var align = this.labelAlign || this.parentLabelAlign();
17237         
17238         if (align ==='left' && this.fieldLabel.length) {
17239
17240             cfg.cn = [
17241                 {
17242                    tag : 'i',
17243                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17244                    tooltip : 'This field is required'
17245                 },
17246                 {
17247                     tag: 'label',
17248                     cls : 'control-label col-form-label',
17249                     html : this.fieldLabel
17250
17251                 },
17252                 {
17253                     cls : 'roo-combobox-wrap ', 
17254                     cn: [
17255                         combobox
17256                     ]
17257                 }
17258             ];
17259             
17260             var labelCfg = cfg.cn[1];
17261             var contentCfg = cfg.cn[2];
17262             
17263
17264             if(this.indicatorpos == 'right'){
17265                 cfg.cn = [
17266                     {
17267                         tag: 'label',
17268                         'for' :  id,
17269                         cls : 'control-label col-form-label',
17270                         cn : [
17271                             {
17272                                 tag : 'span',
17273                                 html : this.fieldLabel
17274                             },
17275                             {
17276                                 tag : 'i',
17277                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17278                                 tooltip : 'This field is required'
17279                             }
17280                         ]
17281                     },
17282                     {
17283                         cls : "roo-combobox-wrap ",
17284                         cn: [
17285                             combobox
17286                         ]
17287                     }
17288
17289                 ];
17290                 
17291                 labelCfg = cfg.cn[0];
17292                 contentCfg = cfg.cn[1];
17293             }
17294             
17295            
17296             
17297             if(this.labelWidth > 12){
17298                 labelCfg.style = "width: " + this.labelWidth + 'px';
17299             }
17300            
17301             if(this.labelWidth < 13 && this.labelmd == 0){
17302                 this.labelmd = this.labelWidth;
17303             }
17304             
17305             if(this.labellg > 0){
17306                 labelCfg.cls += ' col-lg-' + this.labellg;
17307                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17308             }
17309             
17310             if(this.labelmd > 0){
17311                 labelCfg.cls += ' col-md-' + this.labelmd;
17312                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17313             }
17314             
17315             if(this.labelsm > 0){
17316                 labelCfg.cls += ' col-sm-' + this.labelsm;
17317                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17318             }
17319             
17320             if(this.labelxs > 0){
17321                 labelCfg.cls += ' col-xs-' + this.labelxs;
17322                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17323             }
17324                 
17325                 
17326         } else if ( this.fieldLabel.length) {
17327             cfg.cn = [
17328                 {
17329                    tag : 'i',
17330                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17331                    tooltip : 'This field is required'
17332                 },
17333                 {
17334                     tag: 'label',
17335                     cls : 'control-label',
17336                     html : this.fieldLabel
17337
17338                 },
17339                 {
17340                     cls : '', 
17341                     cn: [
17342                         combobox
17343                     ]
17344                 }
17345             ];
17346             
17347             if(this.indicatorpos == 'right'){
17348                 cfg.cn = [
17349                     {
17350                         tag: 'label',
17351                         cls : 'control-label',
17352                         html : this.fieldLabel,
17353                         cn : [
17354                             {
17355                                tag : 'i',
17356                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17357                                tooltip : 'This field is required'
17358                             }
17359                         ]
17360                     },
17361                     {
17362                         cls : '', 
17363                         cn: [
17364                             combobox
17365                         ]
17366                     }
17367                 ];
17368             }
17369         } else {
17370             cfg.cn = combobox;    
17371         }
17372         
17373         
17374         var settings = this;
17375         
17376         ['xs','sm','md','lg'].map(function(size){
17377             if (settings[size]) {
17378                 cfg.cls += ' col-' + size + '-' + settings[size];
17379             }
17380         });
17381         
17382         return cfg;
17383     },
17384     
17385     initTouchView : function()
17386     {
17387         this.renderTouchView();
17388         
17389         this.touchViewEl.on('scroll', function(){
17390             this.el.dom.scrollTop = 0;
17391         }, this);
17392         
17393         this.originalValue = this.getValue();
17394         
17395         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17396         
17397         this.inputEl().on("click", this.showTouchView, this);
17398         if (this.triggerEl) {
17399             this.triggerEl.on("click", this.showTouchView, this);
17400         }
17401         
17402         
17403         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17404         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17405         
17406         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17407         
17408         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17409         this.store.on('load', this.onTouchViewLoad, this);
17410         this.store.on('loadexception', this.onTouchViewLoadException, this);
17411         
17412         if(this.hiddenName){
17413             
17414             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17415             
17416             this.hiddenField.dom.value =
17417                 this.hiddenValue !== undefined ? this.hiddenValue :
17418                 this.value !== undefined ? this.value : '';
17419         
17420             this.el.dom.removeAttribute('name');
17421             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17422         }
17423         
17424         if(this.multiple){
17425             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17426             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17427         }
17428         
17429         if(this.removable && !this.multiple){
17430             var close = this.closeTriggerEl();
17431             if(close){
17432                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17433                 close.on('click', this.removeBtnClick, this, close);
17434             }
17435         }
17436         /*
17437          * fix the bug in Safari iOS8
17438          */
17439         this.inputEl().on("focus", function(e){
17440             document.activeElement.blur();
17441         }, this);
17442         
17443         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17444         
17445         return;
17446         
17447         
17448     },
17449     
17450     renderTouchView : function()
17451     {
17452         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17453         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17454         
17455         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17456         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17457         
17458         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17459         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17460         this.touchViewBodyEl.setStyle('overflow', 'auto');
17461         
17462         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17463         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17464         
17465         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17466         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17467         
17468     },
17469     
17470     showTouchView : function()
17471     {
17472         if(this.disabled){
17473             return;
17474         }
17475         
17476         this.touchViewHeaderEl.hide();
17477
17478         if(this.modalTitle.length){
17479             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17480             this.touchViewHeaderEl.show();
17481         }
17482
17483         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17484         this.touchViewEl.show();
17485
17486         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17487         
17488         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17489         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17490
17491         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17492
17493         if(this.modalTitle.length){
17494             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17495         }
17496         
17497         this.touchViewBodyEl.setHeight(bodyHeight);
17498
17499         if(this.animate){
17500             var _this = this;
17501             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17502         }else{
17503             this.touchViewEl.addClass(['in','show']);
17504         }
17505         
17506         if(this._touchViewMask){
17507             Roo.get(document.body).addClass("x-body-masked");
17508             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17509             this._touchViewMask.setStyle('z-index', 10000);
17510             this._touchViewMask.addClass('show');
17511         }
17512         
17513         this.doTouchViewQuery();
17514         
17515     },
17516     
17517     hideTouchView : function()
17518     {
17519         this.touchViewEl.removeClass(['in','show']);
17520
17521         if(this.animate){
17522             var _this = this;
17523             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17524         }else{
17525             this.touchViewEl.setStyle('display', 'none');
17526         }
17527         
17528         if(this._touchViewMask){
17529             this._touchViewMask.removeClass('show');
17530             Roo.get(document.body).removeClass("x-body-masked");
17531         }
17532     },
17533     
17534     setTouchViewValue : function()
17535     {
17536         if(this.multiple){
17537             this.clearItem();
17538         
17539             var _this = this;
17540
17541             Roo.each(this.tickItems, function(o){
17542                 this.addItem(o);
17543             }, this);
17544         }
17545         
17546         this.hideTouchView();
17547     },
17548     
17549     doTouchViewQuery : function()
17550     {
17551         var qe = {
17552             query: '',
17553             forceAll: true,
17554             combo: this,
17555             cancel:false
17556         };
17557         
17558         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17559             return false;
17560         }
17561         
17562         if(!this.alwaysQuery || this.mode == 'local'){
17563             this.onTouchViewLoad();
17564             return;
17565         }
17566         
17567         this.store.load();
17568     },
17569     
17570     onTouchViewBeforeLoad : function(combo,opts)
17571     {
17572         return;
17573     },
17574
17575     // private
17576     onTouchViewLoad : function()
17577     {
17578         if(this.store.getCount() < 1){
17579             this.onTouchViewEmptyResults();
17580             return;
17581         }
17582         
17583         this.clearTouchView();
17584         
17585         var rawValue = this.getRawValue();
17586         
17587         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17588         
17589         this.tickItems = [];
17590         
17591         this.store.data.each(function(d, rowIndex){
17592             var row = this.touchViewListGroup.createChild(template);
17593             
17594             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17595                 row.addClass(d.data.cls);
17596             }
17597             
17598             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17599                 var cfg = {
17600                     data : d.data,
17601                     html : d.data[this.displayField]
17602                 };
17603                 
17604                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17605                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17606                 }
17607             }
17608             row.removeClass('selected');
17609             if(!this.multiple && this.valueField &&
17610                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17611             {
17612                 // radio buttons..
17613                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17614                 row.addClass('selected');
17615             }
17616             
17617             if(this.multiple && this.valueField &&
17618                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17619             {
17620                 
17621                 // checkboxes...
17622                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17623                 this.tickItems.push(d.data);
17624             }
17625             
17626             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17627             
17628         }, this);
17629         
17630         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17631         
17632         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17633
17634         if(this.modalTitle.length){
17635             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17636         }
17637
17638         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17639         
17640         if(this.mobile_restrict_height && listHeight < bodyHeight){
17641             this.touchViewBodyEl.setHeight(listHeight);
17642         }
17643         
17644         var _this = this;
17645         
17646         if(firstChecked && listHeight > bodyHeight){
17647             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17648         }
17649         
17650     },
17651     
17652     onTouchViewLoadException : function()
17653     {
17654         this.hideTouchView();
17655     },
17656     
17657     onTouchViewEmptyResults : function()
17658     {
17659         this.clearTouchView();
17660         
17661         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17662         
17663         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17664         
17665     },
17666     
17667     clearTouchView : function()
17668     {
17669         this.touchViewListGroup.dom.innerHTML = '';
17670     },
17671     
17672     onTouchViewClick : function(e, el, o)
17673     {
17674         e.preventDefault();
17675         
17676         var row = o.row;
17677         var rowIndex = o.rowIndex;
17678         
17679         var r = this.store.getAt(rowIndex);
17680         
17681         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17682             
17683             if(!this.multiple){
17684                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17685                     c.dom.removeAttribute('checked');
17686                 }, this);
17687
17688                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17689
17690                 this.setFromData(r.data);
17691
17692                 var close = this.closeTriggerEl();
17693
17694                 if(close){
17695                     close.show();
17696                 }
17697
17698                 this.hideTouchView();
17699
17700                 this.fireEvent('select', this, r, rowIndex);
17701
17702                 return;
17703             }
17704
17705             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17706                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17707                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17708                 return;
17709             }
17710
17711             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17712             this.addItem(r.data);
17713             this.tickItems.push(r.data);
17714         }
17715     },
17716     
17717     getAutoCreateNativeIOS : function()
17718     {
17719         var cfg = {
17720             cls: 'form-group' //input-group,
17721         };
17722         
17723         var combobox =  {
17724             tag: 'select',
17725             cls : 'roo-ios-select'
17726         };
17727         
17728         if (this.name) {
17729             combobox.name = this.name;
17730         }
17731         
17732         if (this.disabled) {
17733             combobox.disabled = true;
17734         }
17735         
17736         var settings = this;
17737         
17738         ['xs','sm','md','lg'].map(function(size){
17739             if (settings[size]) {
17740                 cfg.cls += ' col-' + size + '-' + settings[size];
17741             }
17742         });
17743         
17744         cfg.cn = combobox;
17745         
17746         return cfg;
17747         
17748     },
17749     
17750     initIOSView : function()
17751     {
17752         this.store.on('load', this.onIOSViewLoad, this);
17753         
17754         return;
17755     },
17756     
17757     onIOSViewLoad : function()
17758     {
17759         if(this.store.getCount() < 1){
17760             return;
17761         }
17762         
17763         this.clearIOSView();
17764         
17765         if(this.allowBlank) {
17766             
17767             var default_text = '-- SELECT --';
17768             
17769             if(this.placeholder.length){
17770                 default_text = this.placeholder;
17771             }
17772             
17773             if(this.emptyTitle.length){
17774                 default_text += ' - ' + this.emptyTitle + ' -';
17775             }
17776             
17777             var opt = this.inputEl().createChild({
17778                 tag: 'option',
17779                 value : 0,
17780                 html : default_text
17781             });
17782             
17783             var o = {};
17784             o[this.valueField] = 0;
17785             o[this.displayField] = default_text;
17786             
17787             this.ios_options.push({
17788                 data : o,
17789                 el : opt
17790             });
17791             
17792         }
17793         
17794         this.store.data.each(function(d, rowIndex){
17795             
17796             var html = '';
17797             
17798             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17799                 html = d.data[this.displayField];
17800             }
17801             
17802             var value = '';
17803             
17804             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17805                 value = d.data[this.valueField];
17806             }
17807             
17808             var option = {
17809                 tag: 'option',
17810                 value : value,
17811                 html : html
17812             };
17813             
17814             if(this.value == d.data[this.valueField]){
17815                 option['selected'] = true;
17816             }
17817             
17818             var opt = this.inputEl().createChild(option);
17819             
17820             this.ios_options.push({
17821                 data : d.data,
17822                 el : opt
17823             });
17824             
17825         }, this);
17826         
17827         this.inputEl().on('change', function(){
17828            this.fireEvent('select', this);
17829         }, this);
17830         
17831     },
17832     
17833     clearIOSView: function()
17834     {
17835         this.inputEl().dom.innerHTML = '';
17836         
17837         this.ios_options = [];
17838     },
17839     
17840     setIOSValue: function(v)
17841     {
17842         this.value = v;
17843         
17844         if(!this.ios_options){
17845             return;
17846         }
17847         
17848         Roo.each(this.ios_options, function(opts){
17849            
17850            opts.el.dom.removeAttribute('selected');
17851            
17852            if(opts.data[this.valueField] != v){
17853                return;
17854            }
17855            
17856            opts.el.dom.setAttribute('selected', true);
17857            
17858         }, this);
17859     }
17860
17861     /** 
17862     * @cfg {Boolean} grow 
17863     * @hide 
17864     */
17865     /** 
17866     * @cfg {Number} growMin 
17867     * @hide 
17868     */
17869     /** 
17870     * @cfg {Number} growMax 
17871     * @hide 
17872     */
17873     /**
17874      * @hide
17875      * @method autoSize
17876      */
17877 });
17878
17879 Roo.apply(Roo.bootstrap.ComboBox,  {
17880     
17881     header : {
17882         tag: 'div',
17883         cls: 'modal-header',
17884         cn: [
17885             {
17886                 tag: 'h4',
17887                 cls: 'modal-title'
17888             }
17889         ]
17890     },
17891     
17892     body : {
17893         tag: 'div',
17894         cls: 'modal-body',
17895         cn: [
17896             {
17897                 tag: 'ul',
17898                 cls: 'list-group'
17899             }
17900         ]
17901     },
17902     
17903     listItemRadio : {
17904         tag: 'li',
17905         cls: 'list-group-item',
17906         cn: [
17907             {
17908                 tag: 'span',
17909                 cls: 'roo-combobox-list-group-item-value'
17910             },
17911             {
17912                 tag: 'div',
17913                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17914                 cn: [
17915                     {
17916                         tag: 'input',
17917                         type: 'radio'
17918                     },
17919                     {
17920                         tag: 'label'
17921                     }
17922                 ]
17923             }
17924         ]
17925     },
17926     
17927     listItemCheckbox : {
17928         tag: 'li',
17929         cls: 'list-group-item',
17930         cn: [
17931             {
17932                 tag: 'span',
17933                 cls: 'roo-combobox-list-group-item-value'
17934             },
17935             {
17936                 tag: 'div',
17937                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17938                 cn: [
17939                     {
17940                         tag: 'input',
17941                         type: 'checkbox'
17942                     },
17943                     {
17944                         tag: 'label'
17945                     }
17946                 ]
17947             }
17948         ]
17949     },
17950     
17951     emptyResult : {
17952         tag: 'div',
17953         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17954     },
17955     
17956     footer : {
17957         tag: 'div',
17958         cls: 'modal-footer',
17959         cn: [
17960             {
17961                 tag: 'div',
17962                 cls: 'row',
17963                 cn: [
17964                     {
17965                         tag: 'div',
17966                         cls: 'col-xs-6 text-left',
17967                         cn: {
17968                             tag: 'button',
17969                             cls: 'btn btn-danger roo-touch-view-cancel',
17970                             html: 'Cancel'
17971                         }
17972                     },
17973                     {
17974                         tag: 'div',
17975                         cls: 'col-xs-6 text-right',
17976                         cn: {
17977                             tag: 'button',
17978                             cls: 'btn btn-success roo-touch-view-ok',
17979                             html: 'OK'
17980                         }
17981                     }
17982                 ]
17983             }
17984         ]
17985         
17986     }
17987 });
17988
17989 Roo.apply(Roo.bootstrap.ComboBox,  {
17990     
17991     touchViewTemplate : {
17992         tag: 'div',
17993         cls: 'modal fade roo-combobox-touch-view',
17994         cn: [
17995             {
17996                 tag: 'div',
17997                 cls: 'modal-dialog',
17998                 style : 'position:fixed', // we have to fix position....
17999                 cn: [
18000                     {
18001                         tag: 'div',
18002                         cls: 'modal-content',
18003                         cn: [
18004                             Roo.bootstrap.ComboBox.header,
18005                             Roo.bootstrap.ComboBox.body,
18006                             Roo.bootstrap.ComboBox.footer
18007                         ]
18008                     }
18009                 ]
18010             }
18011         ]
18012     }
18013 });/*
18014  * Based on:
18015  * Ext JS Library 1.1.1
18016  * Copyright(c) 2006-2007, Ext JS, LLC.
18017  *
18018  * Originally Released Under LGPL - original licence link has changed is not relivant.
18019  *
18020  * Fork - LGPL
18021  * <script type="text/javascript">
18022  */
18023
18024 /**
18025  * @class Roo.View
18026  * @extends Roo.util.Observable
18027  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18028  * This class also supports single and multi selection modes. <br>
18029  * Create a data model bound view:
18030  <pre><code>
18031  var store = new Roo.data.Store(...);
18032
18033  var view = new Roo.View({
18034     el : "my-element",
18035     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18036  
18037     singleSelect: true,
18038     selectedClass: "ydataview-selected",
18039     store: store
18040  });
18041
18042  // listen for node click?
18043  view.on("click", function(vw, index, node, e){
18044  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18045  });
18046
18047  // load XML data
18048  dataModel.load("foobar.xml");
18049  </code></pre>
18050  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18051  * <br><br>
18052  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18053  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18054  * 
18055  * Note: old style constructor is still suported (container, template, config)
18056  * 
18057  * @constructor
18058  * Create a new View
18059  * @param {Object} config The config object
18060  * 
18061  */
18062 Roo.View = function(config, depreciated_tpl, depreciated_config){
18063     
18064     this.parent = false;
18065     
18066     if (typeof(depreciated_tpl) == 'undefined') {
18067         // new way.. - universal constructor.
18068         Roo.apply(this, config);
18069         this.el  = Roo.get(this.el);
18070     } else {
18071         // old format..
18072         this.el  = Roo.get(config);
18073         this.tpl = depreciated_tpl;
18074         Roo.apply(this, depreciated_config);
18075     }
18076     this.wrapEl  = this.el.wrap().wrap();
18077     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18078     
18079     
18080     if(typeof(this.tpl) == "string"){
18081         this.tpl = new Roo.Template(this.tpl);
18082     } else {
18083         // support xtype ctors..
18084         this.tpl = new Roo.factory(this.tpl, Roo);
18085     }
18086     
18087     
18088     this.tpl.compile();
18089     
18090     /** @private */
18091     this.addEvents({
18092         /**
18093          * @event beforeclick
18094          * Fires before a click is processed. Returns false to cancel the default action.
18095          * @param {Roo.View} this
18096          * @param {Number} index The index of the target node
18097          * @param {HTMLElement} node The target node
18098          * @param {Roo.EventObject} e The raw event object
18099          */
18100             "beforeclick" : true,
18101         /**
18102          * @event click
18103          * Fires when a template node is clicked.
18104          * @param {Roo.View} this
18105          * @param {Number} index The index of the target node
18106          * @param {HTMLElement} node The target node
18107          * @param {Roo.EventObject} e The raw event object
18108          */
18109             "click" : true,
18110         /**
18111          * @event dblclick
18112          * Fires when a template node is double clicked.
18113          * @param {Roo.View} this
18114          * @param {Number} index The index of the target node
18115          * @param {HTMLElement} node The target node
18116          * @param {Roo.EventObject} e The raw event object
18117          */
18118             "dblclick" : true,
18119         /**
18120          * @event contextmenu
18121          * Fires when a template node is right clicked.
18122          * @param {Roo.View} this
18123          * @param {Number} index The index of the target node
18124          * @param {HTMLElement} node The target node
18125          * @param {Roo.EventObject} e The raw event object
18126          */
18127             "contextmenu" : true,
18128         /**
18129          * @event selectionchange
18130          * Fires when the selected nodes change.
18131          * @param {Roo.View} this
18132          * @param {Array} selections Array of the selected nodes
18133          */
18134             "selectionchange" : true,
18135     
18136         /**
18137          * @event beforeselect
18138          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18139          * @param {Roo.View} this
18140          * @param {HTMLElement} node The node to be selected
18141          * @param {Array} selections Array of currently selected nodes
18142          */
18143             "beforeselect" : true,
18144         /**
18145          * @event preparedata
18146          * Fires on every row to render, to allow you to change the data.
18147          * @param {Roo.View} this
18148          * @param {Object} data to be rendered (change this)
18149          */
18150           "preparedata" : true
18151           
18152           
18153         });
18154
18155
18156
18157     this.el.on({
18158         "click": this.onClick,
18159         "dblclick": this.onDblClick,
18160         "contextmenu": this.onContextMenu,
18161         scope:this
18162     });
18163
18164     this.selections = [];
18165     this.nodes = [];
18166     this.cmp = new Roo.CompositeElementLite([]);
18167     if(this.store){
18168         this.store = Roo.factory(this.store, Roo.data);
18169         this.setStore(this.store, true);
18170     }
18171     
18172     if ( this.footer && this.footer.xtype) {
18173            
18174          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18175         
18176         this.footer.dataSource = this.store;
18177         this.footer.container = fctr;
18178         this.footer = Roo.factory(this.footer, Roo);
18179         fctr.insertFirst(this.el);
18180         
18181         // this is a bit insane - as the paging toolbar seems to detach the el..
18182 //        dom.parentNode.parentNode.parentNode
18183          // they get detached?
18184     }
18185     
18186     
18187     Roo.View.superclass.constructor.call(this);
18188     
18189     
18190 };
18191
18192 Roo.extend(Roo.View, Roo.util.Observable, {
18193     
18194      /**
18195      * @cfg {Roo.data.Store} store Data store to load data from.
18196      */
18197     store : false,
18198     
18199     /**
18200      * @cfg {String|Roo.Element} el The container element.
18201      */
18202     el : '',
18203     
18204     /**
18205      * @cfg {String|Roo.Template} tpl The template used by this View 
18206      */
18207     tpl : false,
18208     /**
18209      * @cfg {String} dataName the named area of the template to use as the data area
18210      *                          Works with domtemplates roo-name="name"
18211      */
18212     dataName: false,
18213     /**
18214      * @cfg {String} selectedClass The css class to add to selected nodes
18215      */
18216     selectedClass : "x-view-selected",
18217      /**
18218      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18219      */
18220     emptyText : "",
18221     
18222     /**
18223      * @cfg {String} text to display on mask (default Loading)
18224      */
18225     mask : false,
18226     /**
18227      * @cfg {Boolean} multiSelect Allow multiple selection
18228      */
18229     multiSelect : false,
18230     /**
18231      * @cfg {Boolean} singleSelect Allow single selection
18232      */
18233     singleSelect:  false,
18234     
18235     /**
18236      * @cfg {Boolean} toggleSelect - selecting 
18237      */
18238     toggleSelect : false,
18239     
18240     /**
18241      * @cfg {Boolean} tickable - selecting 
18242      */
18243     tickable : false,
18244     
18245     /**
18246      * Returns the element this view is bound to.
18247      * @return {Roo.Element}
18248      */
18249     getEl : function(){
18250         return this.wrapEl;
18251     },
18252     
18253     
18254
18255     /**
18256      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18257      */
18258     refresh : function(){
18259         //Roo.log('refresh');
18260         var t = this.tpl;
18261         
18262         // if we are using something like 'domtemplate', then
18263         // the what gets used is:
18264         // t.applySubtemplate(NAME, data, wrapping data..)
18265         // the outer template then get' applied with
18266         //     the store 'extra data'
18267         // and the body get's added to the
18268         //      roo-name="data" node?
18269         //      <span class='roo-tpl-{name}'></span> ?????
18270         
18271         
18272         
18273         this.clearSelections();
18274         this.el.update("");
18275         var html = [];
18276         var records = this.store.getRange();
18277         if(records.length < 1) {
18278             
18279             // is this valid??  = should it render a template??
18280             
18281             this.el.update(this.emptyText);
18282             return;
18283         }
18284         var el = this.el;
18285         if (this.dataName) {
18286             this.el.update(t.apply(this.store.meta)); //????
18287             el = this.el.child('.roo-tpl-' + this.dataName);
18288         }
18289         
18290         for(var i = 0, len = records.length; i < len; i++){
18291             var data = this.prepareData(records[i].data, i, records[i]);
18292             this.fireEvent("preparedata", this, data, i, records[i]);
18293             
18294             var d = Roo.apply({}, data);
18295             
18296             if(this.tickable){
18297                 Roo.apply(d, {'roo-id' : Roo.id()});
18298                 
18299                 var _this = this;
18300             
18301                 Roo.each(this.parent.item, function(item){
18302                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18303                         return;
18304                     }
18305                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18306                 });
18307             }
18308             
18309             html[html.length] = Roo.util.Format.trim(
18310                 this.dataName ?
18311                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18312                     t.apply(d)
18313             );
18314         }
18315         
18316         
18317         
18318         el.update(html.join(""));
18319         this.nodes = el.dom.childNodes;
18320         this.updateIndexes(0);
18321     },
18322     
18323
18324     /**
18325      * Function to override to reformat the data that is sent to
18326      * the template for each node.
18327      * DEPRICATED - use the preparedata event handler.
18328      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18329      * a JSON object for an UpdateManager bound view).
18330      */
18331     prepareData : function(data, index, record)
18332     {
18333         this.fireEvent("preparedata", this, data, index, record);
18334         return data;
18335     },
18336
18337     onUpdate : function(ds, record){
18338         // Roo.log('on update');   
18339         this.clearSelections();
18340         var index = this.store.indexOf(record);
18341         var n = this.nodes[index];
18342         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18343         n.parentNode.removeChild(n);
18344         this.updateIndexes(index, index);
18345     },
18346
18347     
18348     
18349 // --------- FIXME     
18350     onAdd : function(ds, records, index)
18351     {
18352         //Roo.log(['on Add', ds, records, index] );        
18353         this.clearSelections();
18354         if(this.nodes.length == 0){
18355             this.refresh();
18356             return;
18357         }
18358         var n = this.nodes[index];
18359         for(var i = 0, len = records.length; i < len; i++){
18360             var d = this.prepareData(records[i].data, i, records[i]);
18361             if(n){
18362                 this.tpl.insertBefore(n, d);
18363             }else{
18364                 
18365                 this.tpl.append(this.el, d);
18366             }
18367         }
18368         this.updateIndexes(index);
18369     },
18370
18371     onRemove : function(ds, record, index){
18372        // Roo.log('onRemove');
18373         this.clearSelections();
18374         var el = this.dataName  ?
18375             this.el.child('.roo-tpl-' + this.dataName) :
18376             this.el; 
18377         
18378         el.dom.removeChild(this.nodes[index]);
18379         this.updateIndexes(index);
18380     },
18381
18382     /**
18383      * Refresh an individual node.
18384      * @param {Number} index
18385      */
18386     refreshNode : function(index){
18387         this.onUpdate(this.store, this.store.getAt(index));
18388     },
18389
18390     updateIndexes : function(startIndex, endIndex){
18391         var ns = this.nodes;
18392         startIndex = startIndex || 0;
18393         endIndex = endIndex || ns.length - 1;
18394         for(var i = startIndex; i <= endIndex; i++){
18395             ns[i].nodeIndex = i;
18396         }
18397     },
18398
18399     /**
18400      * Changes the data store this view uses and refresh the view.
18401      * @param {Store} store
18402      */
18403     setStore : function(store, initial){
18404         if(!initial && this.store){
18405             this.store.un("datachanged", this.refresh);
18406             this.store.un("add", this.onAdd);
18407             this.store.un("remove", this.onRemove);
18408             this.store.un("update", this.onUpdate);
18409             this.store.un("clear", this.refresh);
18410             this.store.un("beforeload", this.onBeforeLoad);
18411             this.store.un("load", this.onLoad);
18412             this.store.un("loadexception", this.onLoad);
18413         }
18414         if(store){
18415           
18416             store.on("datachanged", this.refresh, this);
18417             store.on("add", this.onAdd, this);
18418             store.on("remove", this.onRemove, this);
18419             store.on("update", this.onUpdate, this);
18420             store.on("clear", this.refresh, this);
18421             store.on("beforeload", this.onBeforeLoad, this);
18422             store.on("load", this.onLoad, this);
18423             store.on("loadexception", this.onLoad, this);
18424         }
18425         
18426         if(store){
18427             this.refresh();
18428         }
18429     },
18430     /**
18431      * onbeforeLoad - masks the loading area.
18432      *
18433      */
18434     onBeforeLoad : function(store,opts)
18435     {
18436          //Roo.log('onBeforeLoad');   
18437         if (!opts.add) {
18438             this.el.update("");
18439         }
18440         this.el.mask(this.mask ? this.mask : "Loading" ); 
18441     },
18442     onLoad : function ()
18443     {
18444         this.el.unmask();
18445     },
18446     
18447
18448     /**
18449      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18450      * @param {HTMLElement} node
18451      * @return {HTMLElement} The template node
18452      */
18453     findItemFromChild : function(node){
18454         var el = this.dataName  ?
18455             this.el.child('.roo-tpl-' + this.dataName,true) :
18456             this.el.dom; 
18457         
18458         if(!node || node.parentNode == el){
18459                     return node;
18460             }
18461             var p = node.parentNode;
18462             while(p && p != el){
18463             if(p.parentNode == el){
18464                 return p;
18465             }
18466             p = p.parentNode;
18467         }
18468             return null;
18469     },
18470
18471     /** @ignore */
18472     onClick : function(e){
18473         var item = this.findItemFromChild(e.getTarget());
18474         if(item){
18475             var index = this.indexOf(item);
18476             if(this.onItemClick(item, index, e) !== false){
18477                 this.fireEvent("click", this, index, item, e);
18478             }
18479         }else{
18480             this.clearSelections();
18481         }
18482     },
18483
18484     /** @ignore */
18485     onContextMenu : function(e){
18486         var item = this.findItemFromChild(e.getTarget());
18487         if(item){
18488             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18489         }
18490     },
18491
18492     /** @ignore */
18493     onDblClick : function(e){
18494         var item = this.findItemFromChild(e.getTarget());
18495         if(item){
18496             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18497         }
18498     },
18499
18500     onItemClick : function(item, index, e)
18501     {
18502         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18503             return false;
18504         }
18505         if (this.toggleSelect) {
18506             var m = this.isSelected(item) ? 'unselect' : 'select';
18507             //Roo.log(m);
18508             var _t = this;
18509             _t[m](item, true, false);
18510             return true;
18511         }
18512         if(this.multiSelect || this.singleSelect){
18513             if(this.multiSelect && e.shiftKey && this.lastSelection){
18514                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18515             }else{
18516                 this.select(item, this.multiSelect && e.ctrlKey);
18517                 this.lastSelection = item;
18518             }
18519             
18520             if(!this.tickable){
18521                 e.preventDefault();
18522             }
18523             
18524         }
18525         return true;
18526     },
18527
18528     /**
18529      * Get the number of selected nodes.
18530      * @return {Number}
18531      */
18532     getSelectionCount : function(){
18533         return this.selections.length;
18534     },
18535
18536     /**
18537      * Get the currently selected nodes.
18538      * @return {Array} An array of HTMLElements
18539      */
18540     getSelectedNodes : function(){
18541         return this.selections;
18542     },
18543
18544     /**
18545      * Get the indexes of the selected nodes.
18546      * @return {Array}
18547      */
18548     getSelectedIndexes : function(){
18549         var indexes = [], s = this.selections;
18550         for(var i = 0, len = s.length; i < len; i++){
18551             indexes.push(s[i].nodeIndex);
18552         }
18553         return indexes;
18554     },
18555
18556     /**
18557      * Clear all selections
18558      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18559      */
18560     clearSelections : function(suppressEvent){
18561         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18562             this.cmp.elements = this.selections;
18563             this.cmp.removeClass(this.selectedClass);
18564             this.selections = [];
18565             if(!suppressEvent){
18566                 this.fireEvent("selectionchange", this, this.selections);
18567             }
18568         }
18569     },
18570
18571     /**
18572      * Returns true if the passed node is selected
18573      * @param {HTMLElement/Number} node The node or node index
18574      * @return {Boolean}
18575      */
18576     isSelected : function(node){
18577         var s = this.selections;
18578         if(s.length < 1){
18579             return false;
18580         }
18581         node = this.getNode(node);
18582         return s.indexOf(node) !== -1;
18583     },
18584
18585     /**
18586      * Selects nodes.
18587      * @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
18588      * @param {Boolean} keepExisting (optional) true to keep existing selections
18589      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18590      */
18591     select : function(nodeInfo, keepExisting, suppressEvent){
18592         if(nodeInfo instanceof Array){
18593             if(!keepExisting){
18594                 this.clearSelections(true);
18595             }
18596             for(var i = 0, len = nodeInfo.length; i < len; i++){
18597                 this.select(nodeInfo[i], true, true);
18598             }
18599             return;
18600         } 
18601         var node = this.getNode(nodeInfo);
18602         if(!node || this.isSelected(node)){
18603             return; // already selected.
18604         }
18605         if(!keepExisting){
18606             this.clearSelections(true);
18607         }
18608         
18609         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18610             Roo.fly(node).addClass(this.selectedClass);
18611             this.selections.push(node);
18612             if(!suppressEvent){
18613                 this.fireEvent("selectionchange", this, this.selections);
18614             }
18615         }
18616         
18617         
18618     },
18619       /**
18620      * Unselects nodes.
18621      * @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
18622      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18623      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18624      */
18625     unselect : function(nodeInfo, keepExisting, suppressEvent)
18626     {
18627         if(nodeInfo instanceof Array){
18628             Roo.each(this.selections, function(s) {
18629                 this.unselect(s, nodeInfo);
18630             }, this);
18631             return;
18632         }
18633         var node = this.getNode(nodeInfo);
18634         if(!node || !this.isSelected(node)){
18635             //Roo.log("not selected");
18636             return; // not selected.
18637         }
18638         // fireevent???
18639         var ns = [];
18640         Roo.each(this.selections, function(s) {
18641             if (s == node ) {
18642                 Roo.fly(node).removeClass(this.selectedClass);
18643
18644                 return;
18645             }
18646             ns.push(s);
18647         },this);
18648         
18649         this.selections= ns;
18650         this.fireEvent("selectionchange", this, this.selections);
18651     },
18652
18653     /**
18654      * Gets a template node.
18655      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18656      * @return {HTMLElement} The node or null if it wasn't found
18657      */
18658     getNode : function(nodeInfo){
18659         if(typeof nodeInfo == "string"){
18660             return document.getElementById(nodeInfo);
18661         }else if(typeof nodeInfo == "number"){
18662             return this.nodes[nodeInfo];
18663         }
18664         return nodeInfo;
18665     },
18666
18667     /**
18668      * Gets a range template nodes.
18669      * @param {Number} startIndex
18670      * @param {Number} endIndex
18671      * @return {Array} An array of nodes
18672      */
18673     getNodes : function(start, end){
18674         var ns = this.nodes;
18675         start = start || 0;
18676         end = typeof end == "undefined" ? ns.length - 1 : end;
18677         var nodes = [];
18678         if(start <= end){
18679             for(var i = start; i <= end; i++){
18680                 nodes.push(ns[i]);
18681             }
18682         } else{
18683             for(var i = start; i >= end; i--){
18684                 nodes.push(ns[i]);
18685             }
18686         }
18687         return nodes;
18688     },
18689
18690     /**
18691      * Finds the index of the passed node
18692      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18693      * @return {Number} The index of the node or -1
18694      */
18695     indexOf : function(node){
18696         node = this.getNode(node);
18697         if(typeof node.nodeIndex == "number"){
18698             return node.nodeIndex;
18699         }
18700         var ns = this.nodes;
18701         for(var i = 0, len = ns.length; i < len; i++){
18702             if(ns[i] == node){
18703                 return i;
18704             }
18705         }
18706         return -1;
18707     }
18708 });
18709 /*
18710  * - LGPL
18711  *
18712  * based on jquery fullcalendar
18713  * 
18714  */
18715
18716 Roo.bootstrap = Roo.bootstrap || {};
18717 /**
18718  * @class Roo.bootstrap.Calendar
18719  * @extends Roo.bootstrap.Component
18720  * Bootstrap Calendar class
18721  * @cfg {Boolean} loadMask (true|false) default false
18722  * @cfg {Object} header generate the user specific header of the calendar, default false
18723
18724  * @constructor
18725  * Create a new Container
18726  * @param {Object} config The config object
18727  */
18728
18729
18730
18731 Roo.bootstrap.Calendar = function(config){
18732     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18733      this.addEvents({
18734         /**
18735              * @event select
18736              * Fires when a date is selected
18737              * @param {DatePicker} this
18738              * @param {Date} date The selected date
18739              */
18740         'select': true,
18741         /**
18742              * @event monthchange
18743              * Fires when the displayed month changes 
18744              * @param {DatePicker} this
18745              * @param {Date} date The selected month
18746              */
18747         'monthchange': true,
18748         /**
18749              * @event evententer
18750              * Fires when mouse over an event
18751              * @param {Calendar} this
18752              * @param {event} Event
18753              */
18754         'evententer': true,
18755         /**
18756              * @event eventleave
18757              * Fires when the mouse leaves an
18758              * @param {Calendar} this
18759              * @param {event}
18760              */
18761         'eventleave': true,
18762         /**
18763              * @event eventclick
18764              * Fires when the mouse click an
18765              * @param {Calendar} this
18766              * @param {event}
18767              */
18768         'eventclick': true
18769         
18770     });
18771
18772 };
18773
18774 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
18775     
18776      /**
18777      * @cfg {Number} startDay
18778      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18779      */
18780     startDay : 0,
18781     
18782     loadMask : false,
18783     
18784     header : false,
18785       
18786     getAutoCreate : function(){
18787         
18788         
18789         var fc_button = function(name, corner, style, content ) {
18790             return Roo.apply({},{
18791                 tag : 'span',
18792                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
18793                          (corner.length ?
18794                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18795                             ''
18796                         ),
18797                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18798                 unselectable: 'on'
18799             });
18800         };
18801         
18802         var header = {};
18803         
18804         if(!this.header){
18805             header = {
18806                 tag : 'table',
18807                 cls : 'fc-header',
18808                 style : 'width:100%',
18809                 cn : [
18810                     {
18811                         tag: 'tr',
18812                         cn : [
18813                             {
18814                                 tag : 'td',
18815                                 cls : 'fc-header-left',
18816                                 cn : [
18817                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
18818                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
18819                                     { tag: 'span', cls: 'fc-header-space' },
18820                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
18821
18822
18823                                 ]
18824                             },
18825
18826                             {
18827                                 tag : 'td',
18828                                 cls : 'fc-header-center',
18829                                 cn : [
18830                                     {
18831                                         tag: 'span',
18832                                         cls: 'fc-header-title',
18833                                         cn : {
18834                                             tag: 'H2',
18835                                             html : 'month / year'
18836                                         }
18837                                     }
18838
18839                                 ]
18840                             },
18841                             {
18842                                 tag : 'td',
18843                                 cls : 'fc-header-right',
18844                                 cn : [
18845                               /*      fc_button('month', 'left', '', 'month' ),
18846                                     fc_button('week', '', '', 'week' ),
18847                                     fc_button('day', 'right', '', 'day' )
18848                                 */    
18849
18850                                 ]
18851                             }
18852
18853                         ]
18854                     }
18855                 ]
18856             };
18857         }
18858         
18859         header = this.header;
18860         
18861        
18862         var cal_heads = function() {
18863             var ret = [];
18864             // fixme - handle this.
18865             
18866             for (var i =0; i < Date.dayNames.length; i++) {
18867                 var d = Date.dayNames[i];
18868                 ret.push({
18869                     tag: 'th',
18870                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18871                     html : d.substring(0,3)
18872                 });
18873                 
18874             }
18875             ret[0].cls += ' fc-first';
18876             ret[6].cls += ' fc-last';
18877             return ret;
18878         };
18879         var cal_cell = function(n) {
18880             return  {
18881                 tag: 'td',
18882                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18883                 cn : [
18884                     {
18885                         cn : [
18886                             {
18887                                 cls: 'fc-day-number',
18888                                 html: 'D'
18889                             },
18890                             {
18891                                 cls: 'fc-day-content',
18892                              
18893                                 cn : [
18894                                      {
18895                                         style: 'position: relative;' // height: 17px;
18896                                     }
18897                                 ]
18898                             }
18899                             
18900                             
18901                         ]
18902                     }
18903                 ]
18904                 
18905             }
18906         };
18907         var cal_rows = function() {
18908             
18909             var ret = [];
18910             for (var r = 0; r < 6; r++) {
18911                 var row= {
18912                     tag : 'tr',
18913                     cls : 'fc-week',
18914                     cn : []
18915                 };
18916                 
18917                 for (var i =0; i < Date.dayNames.length; i++) {
18918                     var d = Date.dayNames[i];
18919                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18920
18921                 }
18922                 row.cn[0].cls+=' fc-first';
18923                 row.cn[0].cn[0].style = 'min-height:90px';
18924                 row.cn[6].cls+=' fc-last';
18925                 ret.push(row);
18926                 
18927             }
18928             ret[0].cls += ' fc-first';
18929             ret[4].cls += ' fc-prev-last';
18930             ret[5].cls += ' fc-last';
18931             return ret;
18932             
18933         };
18934         
18935         var cal_table = {
18936             tag: 'table',
18937             cls: 'fc-border-separate',
18938             style : 'width:100%',
18939             cellspacing  : 0,
18940             cn : [
18941                 { 
18942                     tag: 'thead',
18943                     cn : [
18944                         { 
18945                             tag: 'tr',
18946                             cls : 'fc-first fc-last',
18947                             cn : cal_heads()
18948                         }
18949                     ]
18950                 },
18951                 { 
18952                     tag: 'tbody',
18953                     cn : cal_rows()
18954                 }
18955                   
18956             ]
18957         };
18958          
18959          var cfg = {
18960             cls : 'fc fc-ltr',
18961             cn : [
18962                 header,
18963                 {
18964                     cls : 'fc-content',
18965                     style : "position: relative;",
18966                     cn : [
18967                         {
18968                             cls : 'fc-view fc-view-month fc-grid',
18969                             style : 'position: relative',
18970                             unselectable : 'on',
18971                             cn : [
18972                                 {
18973                                     cls : 'fc-event-container',
18974                                     style : 'position:absolute;z-index:8;top:0;left:0;'
18975                                 },
18976                                 cal_table
18977                             ]
18978                         }
18979                     ]
18980     
18981                 }
18982            ] 
18983             
18984         };
18985         
18986          
18987         
18988         return cfg;
18989     },
18990     
18991     
18992     initEvents : function()
18993     {
18994         if(!this.store){
18995             throw "can not find store for calendar";
18996         }
18997         
18998         var mark = {
18999             tag: "div",
19000             cls:"x-dlg-mask",
19001             style: "text-align:center",
19002             cn: [
19003                 {
19004                     tag: "div",
19005                     style: "background-color:white;width:50%;margin:250 auto",
19006                     cn: [
19007                         {
19008                             tag: "img",
19009                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
19010                         },
19011                         {
19012                             tag: "span",
19013                             html: "Loading"
19014                         }
19015                         
19016                     ]
19017                 }
19018             ]
19019         };
19020         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19021         
19022         var size = this.el.select('.fc-content', true).first().getSize();
19023         this.maskEl.setSize(size.width, size.height);
19024         this.maskEl.enableDisplayMode("block");
19025         if(!this.loadMask){
19026             this.maskEl.hide();
19027         }
19028         
19029         this.store = Roo.factory(this.store, Roo.data);
19030         this.store.on('load', this.onLoad, this);
19031         this.store.on('beforeload', this.onBeforeLoad, this);
19032         
19033         this.resize();
19034         
19035         this.cells = this.el.select('.fc-day',true);
19036         //Roo.log(this.cells);
19037         this.textNodes = this.el.query('.fc-day-number');
19038         this.cells.addClassOnOver('fc-state-hover');
19039         
19040         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19041         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19042         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19043         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19044         
19045         this.on('monthchange', this.onMonthChange, this);
19046         
19047         this.update(new Date().clearTime());
19048     },
19049     
19050     resize : function() {
19051         var sz  = this.el.getSize();
19052         
19053         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19054         this.el.select('.fc-day-content div',true).setHeight(34);
19055     },
19056     
19057     
19058     // private
19059     showPrevMonth : function(e){
19060         this.update(this.activeDate.add("mo", -1));
19061     },
19062     showToday : function(e){
19063         this.update(new Date().clearTime());
19064     },
19065     // private
19066     showNextMonth : function(e){
19067         this.update(this.activeDate.add("mo", 1));
19068     },
19069
19070     // private
19071     showPrevYear : function(){
19072         this.update(this.activeDate.add("y", -1));
19073     },
19074
19075     // private
19076     showNextYear : function(){
19077         this.update(this.activeDate.add("y", 1));
19078     },
19079
19080     
19081    // private
19082     update : function(date)
19083     {
19084         var vd = this.activeDate;
19085         this.activeDate = date;
19086 //        if(vd && this.el){
19087 //            var t = date.getTime();
19088 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19089 //                Roo.log('using add remove');
19090 //                
19091 //                this.fireEvent('monthchange', this, date);
19092 //                
19093 //                this.cells.removeClass("fc-state-highlight");
19094 //                this.cells.each(function(c){
19095 //                   if(c.dateValue == t){
19096 //                       c.addClass("fc-state-highlight");
19097 //                       setTimeout(function(){
19098 //                            try{c.dom.firstChild.focus();}catch(e){}
19099 //                       }, 50);
19100 //                       return false;
19101 //                   }
19102 //                   return true;
19103 //                });
19104 //                return;
19105 //            }
19106 //        }
19107         
19108         var days = date.getDaysInMonth();
19109         
19110         var firstOfMonth = date.getFirstDateOfMonth();
19111         var startingPos = firstOfMonth.getDay()-this.startDay;
19112         
19113         if(startingPos < this.startDay){
19114             startingPos += 7;
19115         }
19116         
19117         var pm = date.add(Date.MONTH, -1);
19118         var prevStart = pm.getDaysInMonth()-startingPos;
19119 //        
19120         this.cells = this.el.select('.fc-day',true);
19121         this.textNodes = this.el.query('.fc-day-number');
19122         this.cells.addClassOnOver('fc-state-hover');
19123         
19124         var cells = this.cells.elements;
19125         var textEls = this.textNodes;
19126         
19127         Roo.each(cells, function(cell){
19128             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19129         });
19130         
19131         days += startingPos;
19132
19133         // convert everything to numbers so it's fast
19134         var day = 86400000;
19135         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19136         //Roo.log(d);
19137         //Roo.log(pm);
19138         //Roo.log(prevStart);
19139         
19140         var today = new Date().clearTime().getTime();
19141         var sel = date.clearTime().getTime();
19142         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19143         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19144         var ddMatch = this.disabledDatesRE;
19145         var ddText = this.disabledDatesText;
19146         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19147         var ddaysText = this.disabledDaysText;
19148         var format = this.format;
19149         
19150         var setCellClass = function(cal, cell){
19151             cell.row = 0;
19152             cell.events = [];
19153             cell.more = [];
19154             //Roo.log('set Cell Class');
19155             cell.title = "";
19156             var t = d.getTime();
19157             
19158             //Roo.log(d);
19159             
19160             cell.dateValue = t;
19161             if(t == today){
19162                 cell.className += " fc-today";
19163                 cell.className += " fc-state-highlight";
19164                 cell.title = cal.todayText;
19165             }
19166             if(t == sel){
19167                 // disable highlight in other month..
19168                 //cell.className += " fc-state-highlight";
19169                 
19170             }
19171             // disabling
19172             if(t < min) {
19173                 cell.className = " fc-state-disabled";
19174                 cell.title = cal.minText;
19175                 return;
19176             }
19177             if(t > max) {
19178                 cell.className = " fc-state-disabled";
19179                 cell.title = cal.maxText;
19180                 return;
19181             }
19182             if(ddays){
19183                 if(ddays.indexOf(d.getDay()) != -1){
19184                     cell.title = ddaysText;
19185                     cell.className = " fc-state-disabled";
19186                 }
19187             }
19188             if(ddMatch && format){
19189                 var fvalue = d.dateFormat(format);
19190                 if(ddMatch.test(fvalue)){
19191                     cell.title = ddText.replace("%0", fvalue);
19192                     cell.className = " fc-state-disabled";
19193                 }
19194             }
19195             
19196             if (!cell.initialClassName) {
19197                 cell.initialClassName = cell.dom.className;
19198             }
19199             
19200             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19201         };
19202
19203         var i = 0;
19204         
19205         for(; i < startingPos; i++) {
19206             textEls[i].innerHTML = (++prevStart);
19207             d.setDate(d.getDate()+1);
19208             
19209             cells[i].className = "fc-past fc-other-month";
19210             setCellClass(this, cells[i]);
19211         }
19212         
19213         var intDay = 0;
19214         
19215         for(; i < days; i++){
19216             intDay = i - startingPos + 1;
19217             textEls[i].innerHTML = (intDay);
19218             d.setDate(d.getDate()+1);
19219             
19220             cells[i].className = ''; // "x-date-active";
19221             setCellClass(this, cells[i]);
19222         }
19223         var extraDays = 0;
19224         
19225         for(; i < 42; i++) {
19226             textEls[i].innerHTML = (++extraDays);
19227             d.setDate(d.getDate()+1);
19228             
19229             cells[i].className = "fc-future fc-other-month";
19230             setCellClass(this, cells[i]);
19231         }
19232         
19233         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19234         
19235         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19236         
19237         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19238         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19239         
19240         if(totalRows != 6){
19241             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19242             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19243         }
19244         
19245         this.fireEvent('monthchange', this, date);
19246         
19247         
19248         /*
19249         if(!this.internalRender){
19250             var main = this.el.dom.firstChild;
19251             var w = main.offsetWidth;
19252             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19253             Roo.fly(main).setWidth(w);
19254             this.internalRender = true;
19255             // opera does not respect the auto grow header center column
19256             // then, after it gets a width opera refuses to recalculate
19257             // without a second pass
19258             if(Roo.isOpera && !this.secondPass){
19259                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19260                 this.secondPass = true;
19261                 this.update.defer(10, this, [date]);
19262             }
19263         }
19264         */
19265         
19266     },
19267     
19268     findCell : function(dt) {
19269         dt = dt.clearTime().getTime();
19270         var ret = false;
19271         this.cells.each(function(c){
19272             //Roo.log("check " +c.dateValue + '?=' + dt);
19273             if(c.dateValue == dt){
19274                 ret = c;
19275                 return false;
19276             }
19277             return true;
19278         });
19279         
19280         return ret;
19281     },
19282     
19283     findCells : function(ev) {
19284         var s = ev.start.clone().clearTime().getTime();
19285        // Roo.log(s);
19286         var e= ev.end.clone().clearTime().getTime();
19287        // Roo.log(e);
19288         var ret = [];
19289         this.cells.each(function(c){
19290              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19291             
19292             if(c.dateValue > e){
19293                 return ;
19294             }
19295             if(c.dateValue < s){
19296                 return ;
19297             }
19298             ret.push(c);
19299         });
19300         
19301         return ret;    
19302     },
19303     
19304 //    findBestRow: function(cells)
19305 //    {
19306 //        var ret = 0;
19307 //        
19308 //        for (var i =0 ; i < cells.length;i++) {
19309 //            ret  = Math.max(cells[i].rows || 0,ret);
19310 //        }
19311 //        return ret;
19312 //        
19313 //    },
19314     
19315     
19316     addItem : function(ev)
19317     {
19318         // look for vertical location slot in
19319         var cells = this.findCells(ev);
19320         
19321 //        ev.row = this.findBestRow(cells);
19322         
19323         // work out the location.
19324         
19325         var crow = false;
19326         var rows = [];
19327         for(var i =0; i < cells.length; i++) {
19328             
19329             cells[i].row = cells[0].row;
19330             
19331             if(i == 0){
19332                 cells[i].row = cells[i].row + 1;
19333             }
19334             
19335             if (!crow) {
19336                 crow = {
19337                     start : cells[i],
19338                     end :  cells[i]
19339                 };
19340                 continue;
19341             }
19342             if (crow.start.getY() == cells[i].getY()) {
19343                 // on same row.
19344                 crow.end = cells[i];
19345                 continue;
19346             }
19347             // different row.
19348             rows.push(crow);
19349             crow = {
19350                 start: cells[i],
19351                 end : cells[i]
19352             };
19353             
19354         }
19355         
19356         rows.push(crow);
19357         ev.els = [];
19358         ev.rows = rows;
19359         ev.cells = cells;
19360         
19361         cells[0].events.push(ev);
19362         
19363         this.calevents.push(ev);
19364     },
19365     
19366     clearEvents: function() {
19367         
19368         if(!this.calevents){
19369             return;
19370         }
19371         
19372         Roo.each(this.cells.elements, function(c){
19373             c.row = 0;
19374             c.events = [];
19375             c.more = [];
19376         });
19377         
19378         Roo.each(this.calevents, function(e) {
19379             Roo.each(e.els, function(el) {
19380                 el.un('mouseenter' ,this.onEventEnter, this);
19381                 el.un('mouseleave' ,this.onEventLeave, this);
19382                 el.remove();
19383             },this);
19384         },this);
19385         
19386         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19387             e.remove();
19388         });
19389         
19390     },
19391     
19392     renderEvents: function()
19393     {   
19394         var _this = this;
19395         
19396         this.cells.each(function(c) {
19397             
19398             if(c.row < 5){
19399                 return;
19400             }
19401             
19402             var ev = c.events;
19403             
19404             var r = 4;
19405             if(c.row != c.events.length){
19406                 r = 4 - (4 - (c.row - c.events.length));
19407             }
19408             
19409             c.events = ev.slice(0, r);
19410             c.more = ev.slice(r);
19411             
19412             if(c.more.length && c.more.length == 1){
19413                 c.events.push(c.more.pop());
19414             }
19415             
19416             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19417             
19418         });
19419             
19420         this.cells.each(function(c) {
19421             
19422             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19423             
19424             
19425             for (var e = 0; e < c.events.length; e++){
19426                 var ev = c.events[e];
19427                 var rows = ev.rows;
19428                 
19429                 for(var i = 0; i < rows.length; i++) {
19430                 
19431                     // how many rows should it span..
19432
19433                     var  cfg = {
19434                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19435                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19436
19437                         unselectable : "on",
19438                         cn : [
19439                             {
19440                                 cls: 'fc-event-inner',
19441                                 cn : [
19442     //                                {
19443     //                                  tag:'span',
19444     //                                  cls: 'fc-event-time',
19445     //                                  html : cells.length > 1 ? '' : ev.time
19446     //                                },
19447                                     {
19448                                       tag:'span',
19449                                       cls: 'fc-event-title',
19450                                       html : String.format('{0}', ev.title)
19451                                     }
19452
19453
19454                                 ]
19455                             },
19456                             {
19457                                 cls: 'ui-resizable-handle ui-resizable-e',
19458                                 html : '&nbsp;&nbsp;&nbsp'
19459                             }
19460
19461                         ]
19462                     };
19463
19464                     if (i == 0) {
19465                         cfg.cls += ' fc-event-start';
19466                     }
19467                     if ((i+1) == rows.length) {
19468                         cfg.cls += ' fc-event-end';
19469                     }
19470
19471                     var ctr = _this.el.select('.fc-event-container',true).first();
19472                     var cg = ctr.createChild(cfg);
19473
19474                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19475                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19476
19477                     var r = (c.more.length) ? 1 : 0;
19478                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19479                     cg.setWidth(ebox.right - sbox.x -2);
19480
19481                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19482                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19483                     cg.on('click', _this.onEventClick, _this, ev);
19484
19485                     ev.els.push(cg);
19486                     
19487                 }
19488                 
19489             }
19490             
19491             
19492             if(c.more.length){
19493                 var  cfg = {
19494                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19495                     style : 'position: absolute',
19496                     unselectable : "on",
19497                     cn : [
19498                         {
19499                             cls: 'fc-event-inner',
19500                             cn : [
19501                                 {
19502                                   tag:'span',
19503                                   cls: 'fc-event-title',
19504                                   html : 'More'
19505                                 }
19506
19507
19508                             ]
19509                         },
19510                         {
19511                             cls: 'ui-resizable-handle ui-resizable-e',
19512                             html : '&nbsp;&nbsp;&nbsp'
19513                         }
19514
19515                     ]
19516                 };
19517
19518                 var ctr = _this.el.select('.fc-event-container',true).first();
19519                 var cg = ctr.createChild(cfg);
19520
19521                 var sbox = c.select('.fc-day-content',true).first().getBox();
19522                 var ebox = c.select('.fc-day-content',true).first().getBox();
19523                 //Roo.log(cg);
19524                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19525                 cg.setWidth(ebox.right - sbox.x -2);
19526
19527                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19528                 
19529             }
19530             
19531         });
19532         
19533         
19534         
19535     },
19536     
19537     onEventEnter: function (e, el,event,d) {
19538         this.fireEvent('evententer', this, el, event);
19539     },
19540     
19541     onEventLeave: function (e, el,event,d) {
19542         this.fireEvent('eventleave', this, el, event);
19543     },
19544     
19545     onEventClick: function (e, el,event,d) {
19546         this.fireEvent('eventclick', this, el, event);
19547     },
19548     
19549     onMonthChange: function () {
19550         this.store.load();
19551     },
19552     
19553     onMoreEventClick: function(e, el, more)
19554     {
19555         var _this = this;
19556         
19557         this.calpopover.placement = 'right';
19558         this.calpopover.setTitle('More');
19559         
19560         this.calpopover.setContent('');
19561         
19562         var ctr = this.calpopover.el.select('.popover-content', true).first();
19563         
19564         Roo.each(more, function(m){
19565             var cfg = {
19566                 cls : 'fc-event-hori fc-event-draggable',
19567                 html : m.title
19568             };
19569             var cg = ctr.createChild(cfg);
19570             
19571             cg.on('click', _this.onEventClick, _this, m);
19572         });
19573         
19574         this.calpopover.show(el);
19575         
19576         
19577     },
19578     
19579     onLoad: function () 
19580     {   
19581         this.calevents = [];
19582         var cal = this;
19583         
19584         if(this.store.getCount() > 0){
19585             this.store.data.each(function(d){
19586                cal.addItem({
19587                     id : d.data.id,
19588                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19589                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19590                     time : d.data.start_time,
19591                     title : d.data.title,
19592                     description : d.data.description,
19593                     venue : d.data.venue
19594                 });
19595             });
19596         }
19597         
19598         this.renderEvents();
19599         
19600         if(this.calevents.length && this.loadMask){
19601             this.maskEl.hide();
19602         }
19603     },
19604     
19605     onBeforeLoad: function()
19606     {
19607         this.clearEvents();
19608         if(this.loadMask){
19609             this.maskEl.show();
19610         }
19611     }
19612 });
19613
19614  
19615  /*
19616  * - LGPL
19617  *
19618  * element
19619  * 
19620  */
19621
19622 /**
19623  * @class Roo.bootstrap.Popover
19624  * @extends Roo.bootstrap.Component
19625  * Bootstrap Popover class
19626  * @cfg {String} html contents of the popover   (or false to use children..)
19627  * @cfg {String} title of popover (or false to hide)
19628  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19629  * @cfg {String} trigger click || hover (or false to trigger manually)
19630  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19631  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19632  *      - if false and it has a 'parent' then it will be automatically added to that element
19633  *      - if string - Roo.get  will be called 
19634  * @cfg {Number} delay - delay before showing
19635  
19636  * @constructor
19637  * Create a new Popover
19638  * @param {Object} config The config object
19639  */
19640
19641 Roo.bootstrap.Popover = function(config){
19642     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19643     
19644     this.addEvents({
19645         // raw events
19646          /**
19647          * @event show
19648          * After the popover show
19649          * 
19650          * @param {Roo.bootstrap.Popover} this
19651          */
19652         "show" : true,
19653         /**
19654          * @event hide
19655          * After the popover hide
19656          * 
19657          * @param {Roo.bootstrap.Popover} this
19658          */
19659         "hide" : true
19660     });
19661 };
19662
19663 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19664     
19665     title: false,
19666     html: false,
19667     
19668     placement : 'right',
19669     trigger : 'hover', // hover
19670     modal : false,
19671     delay : 0,
19672     
19673     over: false,
19674     
19675     can_build_overlaid : false,
19676     
19677     maskEl : false, // the mask element
19678     headerEl : false,
19679     contentEl : false,
19680     alignEl : false, // when show is called with an element - this get's stored.
19681     
19682     getChildContainer : function()
19683     {
19684         return this.contentEl;
19685         
19686     },
19687     getPopoverHeader : function()
19688     {
19689         this.title = true; // flag not to hide it..
19690         this.headerEl.addClass('p-0');
19691         return this.headerEl
19692     },
19693     
19694     
19695     getAutoCreate : function(){
19696          
19697         var cfg = {
19698            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19699            style: 'display:block',
19700            cn : [
19701                 {
19702                     cls : 'arrow'
19703                 },
19704                 {
19705                     cls : 'popover-inner ',
19706                     cn : [
19707                         {
19708                             tag: 'h3',
19709                             cls: 'popover-title popover-header',
19710                             html : this.title === false ? '' : this.title
19711                         },
19712                         {
19713                             cls : 'popover-content popover-body '  + (this.cls || ''),
19714                             html : this.html || ''
19715                         }
19716                     ]
19717                     
19718                 }
19719            ]
19720         };
19721         
19722         return cfg;
19723     },
19724     /**
19725      * @param {string} the title
19726      */
19727     setTitle: function(str)
19728     {
19729         this.title = str;
19730         if (this.el) {
19731             this.headerEl.dom.innerHTML = str;
19732         }
19733         
19734     },
19735     /**
19736      * @param {string} the body content
19737      */
19738     setContent: function(str)
19739     {
19740         this.html = str;
19741         if (this.contentEl) {
19742             this.contentEl.dom.innerHTML = str;
19743         }
19744         
19745     },
19746     // as it get's added to the bottom of the page.
19747     onRender : function(ct, position)
19748     {
19749         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19750         
19751         
19752         
19753         if(!this.el){
19754             var cfg = Roo.apply({},  this.getAutoCreate());
19755             cfg.id = Roo.id();
19756             
19757             if (this.cls) {
19758                 cfg.cls += ' ' + this.cls;
19759             }
19760             if (this.style) {
19761                 cfg.style = this.style;
19762             }
19763             //Roo.log("adding to ");
19764             this.el = Roo.get(document.body).createChild(cfg, position);
19765 //            Roo.log(this.el);
19766         }
19767         
19768         this.contentEl = this.el.select('.popover-content',true).first();
19769         this.headerEl =  this.el.select('.popover-title',true).first();
19770         
19771         var nitems = [];
19772         if(typeof(this.items) != 'undefined'){
19773             var items = this.items;
19774             delete this.items;
19775
19776             for(var i =0;i < items.length;i++) {
19777                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19778             }
19779         }
19780
19781         this.items = nitems;
19782         
19783         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19784         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19785         
19786         
19787         
19788         this.initEvents();
19789     },
19790     
19791     resizeMask : function()
19792     {
19793         this.maskEl.setSize(
19794             Roo.lib.Dom.getViewWidth(true),
19795             Roo.lib.Dom.getViewHeight(true)
19796         );
19797     },
19798     
19799     initEvents : function()
19800     {
19801         
19802         if (!this.modal) { 
19803             Roo.bootstrap.Popover.register(this);
19804         }
19805          
19806         this.arrowEl = this.el.select('.arrow',true).first();
19807         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19808         this.el.enableDisplayMode('block');
19809         this.el.hide();
19810  
19811         
19812         if (this.over === false && !this.parent()) {
19813             return; 
19814         }
19815         if (this.triggers === false) {
19816             return;
19817         }
19818          
19819         // support parent
19820         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19821         var triggers = this.trigger ? this.trigger.split(' ') : [];
19822         Roo.each(triggers, function(trigger) {
19823         
19824             if (trigger == 'click') {
19825                 on_el.on('click', this.toggle, this);
19826             } else if (trigger != 'manual') {
19827                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19828                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19829       
19830                 on_el.on(eventIn  ,this.enter, this);
19831                 on_el.on(eventOut, this.leave, this);
19832             }
19833         }, this);
19834     },
19835     
19836     
19837     // private
19838     timeout : null,
19839     hoverState : null,
19840     
19841     toggle : function () {
19842         this.hoverState == 'in' ? this.leave() : this.enter();
19843     },
19844     
19845     enter : function () {
19846         
19847         clearTimeout(this.timeout);
19848     
19849         this.hoverState = 'in';
19850     
19851         if (!this.delay || !this.delay.show) {
19852             this.show();
19853             return;
19854         }
19855         var _t = this;
19856         this.timeout = setTimeout(function () {
19857             if (_t.hoverState == 'in') {
19858                 _t.show();
19859             }
19860         }, this.delay.show)
19861     },
19862     
19863     leave : function() {
19864         clearTimeout(this.timeout);
19865     
19866         this.hoverState = 'out';
19867     
19868         if (!this.delay || !this.delay.hide) {
19869             this.hide();
19870             return;
19871         }
19872         var _t = this;
19873         this.timeout = setTimeout(function () {
19874             if (_t.hoverState == 'out') {
19875                 _t.hide();
19876             }
19877         }, this.delay.hide)
19878     },
19879     /**
19880      * Show the popover
19881      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
19882      * @param {string} (left|right|top|bottom) position
19883      */
19884     show : function (on_el, placement)
19885     {
19886         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
19887         on_el = on_el || false; // default to false
19888          
19889         if (!on_el) {
19890             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19891                 on_el = this.parent().el;
19892             } else if (this.over) {
19893                 Roo.get(this.over);
19894             }
19895             
19896         }
19897         
19898         this.alignEl = Roo.get( on_el );
19899
19900         if (!this.el) {
19901             this.render(document.body);
19902         }
19903         
19904         
19905          
19906         
19907         if (this.title === false) {
19908             this.headerEl.hide();
19909         }
19910         
19911        
19912         this.el.show();
19913         this.el.dom.style.display = 'block';
19914          
19915  
19916         if (this.alignEl) {
19917             this.updatePosition(this.placement, true);
19918              
19919         } else {
19920             // this is usually just done by the builder = to show the popoup in the middle of the scren.
19921             var es = this.el.getSize();
19922             var x = Roo.lib.Dom.getViewWidth()/2;
19923             var y = Roo.lib.Dom.getViewHeight()/2;
19924             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
19925             
19926         }
19927
19928         
19929         //var arrow = this.el.select('.arrow',true).first();
19930         //arrow.set(align[2], 
19931         
19932         this.el.addClass('in');
19933         
19934          
19935         
19936         this.hoverState = 'in';
19937         
19938         if (this.modal) {
19939             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19940             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19941             this.maskEl.dom.style.display = 'block';
19942             this.maskEl.addClass('show');
19943         }
19944         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19945  
19946         this.fireEvent('show', this);
19947         
19948     },
19949     /**
19950      * fire this manually after loading a grid in the table for example
19951      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
19952      * @param {Boolean} try and move it if we cant get right position.
19953      */
19954     updatePosition : function(placement, try_move)
19955     {
19956         // allow for calling with no parameters
19957         placement = placement   ? placement :  this.placement;
19958         try_move = typeof(try_move) == 'undefined' ? true : try_move;
19959         
19960         this.el.removeClass([
19961             'fade','top','bottom', 'left', 'right','in',
19962             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19963         ]);
19964         this.el.addClass(placement + ' bs-popover-' + placement);
19965         
19966         if (!this.alignEl ) {
19967             return false;
19968         }
19969         
19970         switch (placement) {
19971             case 'right':
19972                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
19973                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
19974                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
19975                     //normal display... or moved up/down.
19976                     this.el.setXY(offset);
19977                     var xy = this.alignEl.getAnchorXY('tr', false);
19978                     xy[0]+=2;xy[1]+=5;
19979                     this.arrowEl.setXY(xy);
19980                     return true;
19981                 }
19982                 // continue through...
19983                 return this.updatePosition('left', false);
19984                 
19985             
19986             case 'left':
19987                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
19988                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
19989                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
19990                     //normal display... or moved up/down.
19991                     this.el.setXY(offset);
19992                     var xy = this.alignEl.getAnchorXY('tl', false);
19993                     xy[0]-=10;xy[1]+=5; // << fix me
19994                     this.arrowEl.setXY(xy);
19995                     return true;
19996                 }
19997                 // call self...
19998                 return this.updatePosition('right', false);
19999             
20000             case 'top':
20001                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20002                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20003                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20004                     //normal display... or moved up/down.
20005                     this.el.setXY(offset);
20006                     var xy = this.alignEl.getAnchorXY('t', false);
20007                     xy[1]-=10; // << fix me
20008                     this.arrowEl.setXY(xy);
20009                     return true;
20010                 }
20011                 // fall through
20012                return this.updatePosition('bottom', false);
20013             
20014             case 'bottom':
20015                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20016                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20017                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20018                     //normal display... or moved up/down.
20019                     this.el.setXY(offset);
20020                     var xy = this.alignEl.getAnchorXY('b', false);
20021                      xy[1]+=2; // << fix me
20022                     this.arrowEl.setXY(xy);
20023                     return true;
20024                 }
20025                 // fall through
20026                 return this.updatePosition('top', false);
20027                 
20028             
20029         }
20030         
20031         
20032         return false;
20033     },
20034     
20035     hide : function()
20036     {
20037         this.el.setXY([0,0]);
20038         this.el.removeClass('in');
20039         this.el.hide();
20040         this.hoverState = null;
20041         this.maskEl.hide(); // always..
20042         this.fireEvent('hide', this);
20043     }
20044     
20045 });
20046
20047
20048 Roo.apply(Roo.bootstrap.Popover, {
20049
20050     alignment : {
20051         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20052         'right' : ['l-br', [10,0], 'right bs-popover-right'],
20053         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20054         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20055     },
20056     
20057     zIndex : 20001,
20058
20059     clickHander : false,
20060     
20061
20062     onMouseDown : function(e)
20063     {
20064         if (!e.getTarget(".roo-popover")) {
20065             this.hideAll();
20066         }
20067          
20068     },
20069     
20070     popups : [],
20071     
20072     register : function(popup)
20073     {
20074         if (!Roo.bootstrap.Popover.clickHandler) {
20075             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20076         }
20077         // hide other popups.
20078         this.hideAll();
20079         this.popups.push(popup);
20080     },
20081     hideAll : function()
20082     {
20083         this.popups.forEach(function(p) {
20084             p.hide();
20085         });
20086     }
20087
20088 });/*
20089  * - LGPL
20090  *
20091  * Card header - holder for the card header elements.
20092  * 
20093  */
20094
20095 /**
20096  * @class Roo.bootstrap.PopoverNav
20097  * @extends Roo.bootstrap.NavGroup
20098  * Bootstrap Popover header navigation class
20099  * @constructor
20100  * Create a new Popover Header Navigation 
20101  * @param {Object} config The config object
20102  */
20103
20104 Roo.bootstrap.PopoverNav = function(config){
20105     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20106 };
20107
20108 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
20109     
20110     
20111     container_method : 'getPopoverHeader' 
20112     
20113      
20114     
20115     
20116    
20117 });
20118
20119  
20120
20121  /*
20122  * - LGPL
20123  *
20124  * Progress
20125  * 
20126  */
20127
20128 /**
20129  * @class Roo.bootstrap.Progress
20130  * @extends Roo.bootstrap.Component
20131  * Bootstrap Progress class
20132  * @cfg {Boolean} striped striped of the progress bar
20133  * @cfg {Boolean} active animated of the progress bar
20134  * 
20135  * 
20136  * @constructor
20137  * Create a new Progress
20138  * @param {Object} config The config object
20139  */
20140
20141 Roo.bootstrap.Progress = function(config){
20142     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20143 };
20144
20145 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20146     
20147     striped : false,
20148     active: false,
20149     
20150     getAutoCreate : function(){
20151         var cfg = {
20152             tag: 'div',
20153             cls: 'progress'
20154         };
20155         
20156         
20157         if(this.striped){
20158             cfg.cls += ' progress-striped';
20159         }
20160       
20161         if(this.active){
20162             cfg.cls += ' active';
20163         }
20164         
20165         
20166         return cfg;
20167     }
20168    
20169 });
20170
20171  
20172
20173  /*
20174  * - LGPL
20175  *
20176  * ProgressBar
20177  * 
20178  */
20179
20180 /**
20181  * @class Roo.bootstrap.ProgressBar
20182  * @extends Roo.bootstrap.Component
20183  * Bootstrap ProgressBar class
20184  * @cfg {Number} aria_valuenow aria-value now
20185  * @cfg {Number} aria_valuemin aria-value min
20186  * @cfg {Number} aria_valuemax aria-value max
20187  * @cfg {String} label label for the progress bar
20188  * @cfg {String} panel (success | info | warning | danger )
20189  * @cfg {String} role role of the progress bar
20190  * @cfg {String} sr_only text
20191  * 
20192  * 
20193  * @constructor
20194  * Create a new ProgressBar
20195  * @param {Object} config The config object
20196  */
20197
20198 Roo.bootstrap.ProgressBar = function(config){
20199     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20200 };
20201
20202 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20203     
20204     aria_valuenow : 0,
20205     aria_valuemin : 0,
20206     aria_valuemax : 100,
20207     label : false,
20208     panel : false,
20209     role : false,
20210     sr_only: false,
20211     
20212     getAutoCreate : function()
20213     {
20214         
20215         var cfg = {
20216             tag: 'div',
20217             cls: 'progress-bar',
20218             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20219         };
20220         
20221         if(this.sr_only){
20222             cfg.cn = {
20223                 tag: 'span',
20224                 cls: 'sr-only',
20225                 html: this.sr_only
20226             }
20227         }
20228         
20229         if(this.role){
20230             cfg.role = this.role;
20231         }
20232         
20233         if(this.aria_valuenow){
20234             cfg['aria-valuenow'] = this.aria_valuenow;
20235         }
20236         
20237         if(this.aria_valuemin){
20238             cfg['aria-valuemin'] = this.aria_valuemin;
20239         }
20240         
20241         if(this.aria_valuemax){
20242             cfg['aria-valuemax'] = this.aria_valuemax;
20243         }
20244         
20245         if(this.label && !this.sr_only){
20246             cfg.html = this.label;
20247         }
20248         
20249         if(this.panel){
20250             cfg.cls += ' progress-bar-' + this.panel;
20251         }
20252         
20253         return cfg;
20254     },
20255     
20256     update : function(aria_valuenow)
20257     {
20258         this.aria_valuenow = aria_valuenow;
20259         
20260         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20261     }
20262    
20263 });
20264
20265  
20266
20267  /*
20268  * - LGPL
20269  *
20270  * column
20271  * 
20272  */
20273
20274 /**
20275  * @class Roo.bootstrap.TabGroup
20276  * @extends Roo.bootstrap.Column
20277  * Bootstrap Column class
20278  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20279  * @cfg {Boolean} carousel true to make the group behave like a carousel
20280  * @cfg {Boolean} bullets show bullets for the panels
20281  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20282  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20283  * @cfg {Boolean} showarrow (true|false) show arrow default true
20284  * 
20285  * @constructor
20286  * Create a new TabGroup
20287  * @param {Object} config The config object
20288  */
20289
20290 Roo.bootstrap.TabGroup = function(config){
20291     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20292     if (!this.navId) {
20293         this.navId = Roo.id();
20294     }
20295     this.tabs = [];
20296     Roo.bootstrap.TabGroup.register(this);
20297     
20298 };
20299
20300 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20301     
20302     carousel : false,
20303     transition : false,
20304     bullets : 0,
20305     timer : 0,
20306     autoslide : false,
20307     slideFn : false,
20308     slideOnTouch : false,
20309     showarrow : true,
20310     
20311     getAutoCreate : function()
20312     {
20313         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20314         
20315         cfg.cls += ' tab-content';
20316         
20317         if (this.carousel) {
20318             cfg.cls += ' carousel slide';
20319             
20320             cfg.cn = [{
20321                cls : 'carousel-inner',
20322                cn : []
20323             }];
20324         
20325             if(this.bullets  && !Roo.isTouch){
20326                 
20327                 var bullets = {
20328                     cls : 'carousel-bullets',
20329                     cn : []
20330                 };
20331                
20332                 if(this.bullets_cls){
20333                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20334                 }
20335                 
20336                 bullets.cn.push({
20337                     cls : 'clear'
20338                 });
20339                 
20340                 cfg.cn[0].cn.push(bullets);
20341             }
20342             
20343             if(this.showarrow){
20344                 cfg.cn[0].cn.push({
20345                     tag : 'div',
20346                     class : 'carousel-arrow',
20347                     cn : [
20348                         {
20349                             tag : 'div',
20350                             class : 'carousel-prev',
20351                             cn : [
20352                                 {
20353                                     tag : 'i',
20354                                     class : 'fa fa-chevron-left'
20355                                 }
20356                             ]
20357                         },
20358                         {
20359                             tag : 'div',
20360                             class : 'carousel-next',
20361                             cn : [
20362                                 {
20363                                     tag : 'i',
20364                                     class : 'fa fa-chevron-right'
20365                                 }
20366                             ]
20367                         }
20368                     ]
20369                 });
20370             }
20371             
20372         }
20373         
20374         return cfg;
20375     },
20376     
20377     initEvents:  function()
20378     {
20379 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20380 //            this.el.on("touchstart", this.onTouchStart, this);
20381 //        }
20382         
20383         if(this.autoslide){
20384             var _this = this;
20385             
20386             this.slideFn = window.setInterval(function() {
20387                 _this.showPanelNext();
20388             }, this.timer);
20389         }
20390         
20391         if(this.showarrow){
20392             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20393             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20394         }
20395         
20396         
20397     },
20398     
20399 //    onTouchStart : function(e, el, o)
20400 //    {
20401 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20402 //            return;
20403 //        }
20404 //        
20405 //        this.showPanelNext();
20406 //    },
20407     
20408     
20409     getChildContainer : function()
20410     {
20411         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20412     },
20413     
20414     /**
20415     * register a Navigation item
20416     * @param {Roo.bootstrap.NavItem} the navitem to add
20417     */
20418     register : function(item)
20419     {
20420         this.tabs.push( item);
20421         item.navId = this.navId; // not really needed..
20422         this.addBullet();
20423     
20424     },
20425     
20426     getActivePanel : function()
20427     {
20428         var r = false;
20429         Roo.each(this.tabs, function(t) {
20430             if (t.active) {
20431                 r = t;
20432                 return false;
20433             }
20434             return null;
20435         });
20436         return r;
20437         
20438     },
20439     getPanelByName : function(n)
20440     {
20441         var r = false;
20442         Roo.each(this.tabs, function(t) {
20443             if (t.tabId == n) {
20444                 r = t;
20445                 return false;
20446             }
20447             return null;
20448         });
20449         return r;
20450     },
20451     indexOfPanel : function(p)
20452     {
20453         var r = false;
20454         Roo.each(this.tabs, function(t,i) {
20455             if (t.tabId == p.tabId) {
20456                 r = i;
20457                 return false;
20458             }
20459             return null;
20460         });
20461         return r;
20462     },
20463     /**
20464      * show a specific panel
20465      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20466      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20467      */
20468     showPanel : function (pan)
20469     {
20470         if(this.transition || typeof(pan) == 'undefined'){
20471             Roo.log("waiting for the transitionend");
20472             return false;
20473         }
20474         
20475         if (typeof(pan) == 'number') {
20476             pan = this.tabs[pan];
20477         }
20478         
20479         if (typeof(pan) == 'string') {
20480             pan = this.getPanelByName(pan);
20481         }
20482         
20483         var cur = this.getActivePanel();
20484         
20485         if(!pan || !cur){
20486             Roo.log('pan or acitve pan is undefined');
20487             return false;
20488         }
20489         
20490         if (pan.tabId == this.getActivePanel().tabId) {
20491             return true;
20492         }
20493         
20494         if (false === cur.fireEvent('beforedeactivate')) {
20495             return false;
20496         }
20497         
20498         if(this.bullets > 0 && !Roo.isTouch){
20499             this.setActiveBullet(this.indexOfPanel(pan));
20500         }
20501         
20502         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20503             
20504             //class="carousel-item carousel-item-next carousel-item-left"
20505             
20506             this.transition = true;
20507             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20508             var lr = dir == 'next' ? 'left' : 'right';
20509             pan.el.addClass(dir); // or prev
20510             pan.el.addClass('carousel-item-' + dir); // or prev
20511             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20512             cur.el.addClass(lr); // or right
20513             pan.el.addClass(lr);
20514             cur.el.addClass('carousel-item-' +lr); // or right
20515             pan.el.addClass('carousel-item-' +lr);
20516             
20517             
20518             var _this = this;
20519             cur.el.on('transitionend', function() {
20520                 Roo.log("trans end?");
20521                 
20522                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20523                 pan.setActive(true);
20524                 
20525                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20526                 cur.setActive(false);
20527                 
20528                 _this.transition = false;
20529                 
20530             }, this, { single:  true } );
20531             
20532             return true;
20533         }
20534         
20535         cur.setActive(false);
20536         pan.setActive(true);
20537         
20538         return true;
20539         
20540     },
20541     showPanelNext : function()
20542     {
20543         var i = this.indexOfPanel(this.getActivePanel());
20544         
20545         if (i >= this.tabs.length - 1 && !this.autoslide) {
20546             return;
20547         }
20548         
20549         if (i >= this.tabs.length - 1 && this.autoslide) {
20550             i = -1;
20551         }
20552         
20553         this.showPanel(this.tabs[i+1]);
20554     },
20555     
20556     showPanelPrev : function()
20557     {
20558         var i = this.indexOfPanel(this.getActivePanel());
20559         
20560         if (i  < 1 && !this.autoslide) {
20561             return;
20562         }
20563         
20564         if (i < 1 && this.autoslide) {
20565             i = this.tabs.length;
20566         }
20567         
20568         this.showPanel(this.tabs[i-1]);
20569     },
20570     
20571     
20572     addBullet: function()
20573     {
20574         if(!this.bullets || Roo.isTouch){
20575             return;
20576         }
20577         var ctr = this.el.select('.carousel-bullets',true).first();
20578         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20579         var bullet = ctr.createChild({
20580             cls : 'bullet bullet-' + i
20581         },ctr.dom.lastChild);
20582         
20583         
20584         var _this = this;
20585         
20586         bullet.on('click', (function(e, el, o, ii, t){
20587
20588             e.preventDefault();
20589
20590             this.showPanel(ii);
20591
20592             if(this.autoslide && this.slideFn){
20593                 clearInterval(this.slideFn);
20594                 this.slideFn = window.setInterval(function() {
20595                     _this.showPanelNext();
20596                 }, this.timer);
20597             }
20598
20599         }).createDelegate(this, [i, bullet], true));
20600                 
20601         
20602     },
20603      
20604     setActiveBullet : function(i)
20605     {
20606         if(Roo.isTouch){
20607             return;
20608         }
20609         
20610         Roo.each(this.el.select('.bullet', true).elements, function(el){
20611             el.removeClass('selected');
20612         });
20613
20614         var bullet = this.el.select('.bullet-' + i, true).first();
20615         
20616         if(!bullet){
20617             return;
20618         }
20619         
20620         bullet.addClass('selected');
20621     }
20622     
20623     
20624   
20625 });
20626
20627  
20628
20629  
20630  
20631 Roo.apply(Roo.bootstrap.TabGroup, {
20632     
20633     groups: {},
20634      /**
20635     * register a Navigation Group
20636     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20637     */
20638     register : function(navgrp)
20639     {
20640         this.groups[navgrp.navId] = navgrp;
20641         
20642     },
20643     /**
20644     * fetch a Navigation Group based on the navigation ID
20645     * if one does not exist , it will get created.
20646     * @param {string} the navgroup to add
20647     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20648     */
20649     get: function(navId) {
20650         if (typeof(this.groups[navId]) == 'undefined') {
20651             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20652         }
20653         return this.groups[navId] ;
20654     }
20655     
20656     
20657     
20658 });
20659
20660  /*
20661  * - LGPL
20662  *
20663  * TabPanel
20664  * 
20665  */
20666
20667 /**
20668  * @class Roo.bootstrap.TabPanel
20669  * @extends Roo.bootstrap.Component
20670  * Bootstrap TabPanel class
20671  * @cfg {Boolean} active panel active
20672  * @cfg {String} html panel content
20673  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20674  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20675  * @cfg {String} href click to link..
20676  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20677  * 
20678  * 
20679  * @constructor
20680  * Create a new TabPanel
20681  * @param {Object} config The config object
20682  */
20683
20684 Roo.bootstrap.TabPanel = function(config){
20685     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20686     this.addEvents({
20687         /**
20688              * @event changed
20689              * Fires when the active status changes
20690              * @param {Roo.bootstrap.TabPanel} this
20691              * @param {Boolean} state the new state
20692             
20693          */
20694         'changed': true,
20695         /**
20696              * @event beforedeactivate
20697              * Fires before a tab is de-activated - can be used to do validation on a form.
20698              * @param {Roo.bootstrap.TabPanel} this
20699              * @return {Boolean} false if there is an error
20700             
20701          */
20702         'beforedeactivate': true
20703      });
20704     
20705     this.tabId = this.tabId || Roo.id();
20706   
20707 };
20708
20709 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20710     
20711     active: false,
20712     html: false,
20713     tabId: false,
20714     navId : false,
20715     href : '',
20716     touchSlide : false,
20717     getAutoCreate : function(){
20718         
20719         
20720         var cfg = {
20721             tag: 'div',
20722             // item is needed for carousel - not sure if it has any effect otherwise
20723             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20724             html: this.html || ''
20725         };
20726         
20727         if(this.active){
20728             cfg.cls += ' active';
20729         }
20730         
20731         if(this.tabId){
20732             cfg.tabId = this.tabId;
20733         }
20734         
20735         
20736         
20737         return cfg;
20738     },
20739     
20740     initEvents:  function()
20741     {
20742         var p = this.parent();
20743         
20744         this.navId = this.navId || p.navId;
20745         
20746         if (typeof(this.navId) != 'undefined') {
20747             // not really needed.. but just in case.. parent should be a NavGroup.
20748             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20749             
20750             tg.register(this);
20751             
20752             var i = tg.tabs.length - 1;
20753             
20754             if(this.active && tg.bullets > 0 && i < tg.bullets){
20755                 tg.setActiveBullet(i);
20756             }
20757         }
20758         
20759         this.el.on('click', this.onClick, this);
20760         
20761         if(Roo.isTouch && this.touchSlide){
20762             this.el.on("touchstart", this.onTouchStart, this);
20763             this.el.on("touchmove", this.onTouchMove, this);
20764             this.el.on("touchend", this.onTouchEnd, this);
20765         }
20766         
20767     },
20768     
20769     onRender : function(ct, position)
20770     {
20771         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20772     },
20773     
20774     setActive : function(state)
20775     {
20776         Roo.log("panel - set active " + this.tabId + "=" + state);
20777         
20778         this.active = state;
20779         if (!state) {
20780             this.el.removeClass('active');
20781             
20782         } else  if (!this.el.hasClass('active')) {
20783             this.el.addClass('active');
20784         }
20785         
20786         this.fireEvent('changed', this, state);
20787     },
20788     
20789     onClick : function(e)
20790     {
20791         e.preventDefault();
20792         
20793         if(!this.href.length){
20794             return;
20795         }
20796         
20797         window.location.href = this.href;
20798     },
20799     
20800     startX : 0,
20801     startY : 0,
20802     endX : 0,
20803     endY : 0,
20804     swiping : false,
20805     
20806     onTouchStart : function(e)
20807     {
20808         this.swiping = false;
20809         
20810         this.startX = e.browserEvent.touches[0].clientX;
20811         this.startY = e.browserEvent.touches[0].clientY;
20812     },
20813     
20814     onTouchMove : function(e)
20815     {
20816         this.swiping = true;
20817         
20818         this.endX = e.browserEvent.touches[0].clientX;
20819         this.endY = e.browserEvent.touches[0].clientY;
20820     },
20821     
20822     onTouchEnd : function(e)
20823     {
20824         if(!this.swiping){
20825             this.onClick(e);
20826             return;
20827         }
20828         
20829         var tabGroup = this.parent();
20830         
20831         if(this.endX > this.startX){ // swiping right
20832             tabGroup.showPanelPrev();
20833             return;
20834         }
20835         
20836         if(this.startX > this.endX){ // swiping left
20837             tabGroup.showPanelNext();
20838             return;
20839         }
20840     }
20841     
20842     
20843 });
20844  
20845
20846  
20847
20848  /*
20849  * - LGPL
20850  *
20851  * DateField
20852  * 
20853  */
20854
20855 /**
20856  * @class Roo.bootstrap.DateField
20857  * @extends Roo.bootstrap.Input
20858  * Bootstrap DateField class
20859  * @cfg {Number} weekStart default 0
20860  * @cfg {String} viewMode default empty, (months|years)
20861  * @cfg {String} minViewMode default empty, (months|years)
20862  * @cfg {Number} startDate default -Infinity
20863  * @cfg {Number} endDate default Infinity
20864  * @cfg {Boolean} todayHighlight default false
20865  * @cfg {Boolean} todayBtn default false
20866  * @cfg {Boolean} calendarWeeks default false
20867  * @cfg {Object} daysOfWeekDisabled default empty
20868  * @cfg {Boolean} singleMode default false (true | false)
20869  * 
20870  * @cfg {Boolean} keyboardNavigation default true
20871  * @cfg {String} language default en
20872  * 
20873  * @constructor
20874  * Create a new DateField
20875  * @param {Object} config The config object
20876  */
20877
20878 Roo.bootstrap.DateField = function(config){
20879     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20880      this.addEvents({
20881             /**
20882              * @event show
20883              * Fires when this field show.
20884              * @param {Roo.bootstrap.DateField} this
20885              * @param {Mixed} date The date value
20886              */
20887             show : true,
20888             /**
20889              * @event show
20890              * Fires when this field hide.
20891              * @param {Roo.bootstrap.DateField} this
20892              * @param {Mixed} date The date value
20893              */
20894             hide : true,
20895             /**
20896              * @event select
20897              * Fires when select a date.
20898              * @param {Roo.bootstrap.DateField} this
20899              * @param {Mixed} date The date value
20900              */
20901             select : true,
20902             /**
20903              * @event beforeselect
20904              * Fires when before select a date.
20905              * @param {Roo.bootstrap.DateField} this
20906              * @param {Mixed} date The date value
20907              */
20908             beforeselect : true
20909         });
20910 };
20911
20912 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20913     
20914     /**
20915      * @cfg {String} format
20916      * The default date format string which can be overriden for localization support.  The format must be
20917      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20918      */
20919     format : "m/d/y",
20920     /**
20921      * @cfg {String} altFormats
20922      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20923      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20924      */
20925     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20926     
20927     weekStart : 0,
20928     
20929     viewMode : '',
20930     
20931     minViewMode : '',
20932     
20933     todayHighlight : false,
20934     
20935     todayBtn: false,
20936     
20937     language: 'en',
20938     
20939     keyboardNavigation: true,
20940     
20941     calendarWeeks: false,
20942     
20943     startDate: -Infinity,
20944     
20945     endDate: Infinity,
20946     
20947     daysOfWeekDisabled: [],
20948     
20949     _events: [],
20950     
20951     singleMode : false,
20952     
20953     UTCDate: function()
20954     {
20955         return new Date(Date.UTC.apply(Date, arguments));
20956     },
20957     
20958     UTCToday: function()
20959     {
20960         var today = new Date();
20961         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20962     },
20963     
20964     getDate: function() {
20965             var d = this.getUTCDate();
20966             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20967     },
20968     
20969     getUTCDate: function() {
20970             return this.date;
20971     },
20972     
20973     setDate: function(d) {
20974             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20975     },
20976     
20977     setUTCDate: function(d) {
20978             this.date = d;
20979             this.setValue(this.formatDate(this.date));
20980     },
20981         
20982     onRender: function(ct, position)
20983     {
20984         
20985         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20986         
20987         this.language = this.language || 'en';
20988         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20989         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20990         
20991         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20992         this.format = this.format || 'm/d/y';
20993         this.isInline = false;
20994         this.isInput = true;
20995         this.component = this.el.select('.add-on', true).first() || false;
20996         this.component = (this.component && this.component.length === 0) ? false : this.component;
20997         this.hasInput = this.component && this.inputEl().length;
20998         
20999         if (typeof(this.minViewMode === 'string')) {
21000             switch (this.minViewMode) {
21001                 case 'months':
21002                     this.minViewMode = 1;
21003                     break;
21004                 case 'years':
21005                     this.minViewMode = 2;
21006                     break;
21007                 default:
21008                     this.minViewMode = 0;
21009                     break;
21010             }
21011         }
21012         
21013         if (typeof(this.viewMode === 'string')) {
21014             switch (this.viewMode) {
21015                 case 'months':
21016                     this.viewMode = 1;
21017                     break;
21018                 case 'years':
21019                     this.viewMode = 2;
21020                     break;
21021                 default:
21022                     this.viewMode = 0;
21023                     break;
21024             }
21025         }
21026                 
21027         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21028         
21029 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21030         
21031         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21032         
21033         this.picker().on('mousedown', this.onMousedown, this);
21034         this.picker().on('click', this.onClick, this);
21035         
21036         this.picker().addClass('datepicker-dropdown');
21037         
21038         this.startViewMode = this.viewMode;
21039         
21040         if(this.singleMode){
21041             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21042                 v.setVisibilityMode(Roo.Element.DISPLAY);
21043                 v.hide();
21044             });
21045             
21046             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21047                 v.setStyle('width', '189px');
21048             });
21049         }
21050         
21051         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21052             if(!this.calendarWeeks){
21053                 v.remove();
21054                 return;
21055             }
21056             
21057             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21058             v.attr('colspan', function(i, val){
21059                 return parseInt(val) + 1;
21060             });
21061         });
21062                         
21063         
21064         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21065         
21066         this.setStartDate(this.startDate);
21067         this.setEndDate(this.endDate);
21068         
21069         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21070         
21071         this.fillDow();
21072         this.fillMonths();
21073         this.update();
21074         this.showMode();
21075         
21076         if(this.isInline) {
21077             this.showPopup();
21078         }
21079     },
21080     
21081     picker : function()
21082     {
21083         return this.pickerEl;
21084 //        return this.el.select('.datepicker', true).first();
21085     },
21086     
21087     fillDow: function()
21088     {
21089         var dowCnt = this.weekStart;
21090         
21091         var dow = {
21092             tag: 'tr',
21093             cn: [
21094                 
21095             ]
21096         };
21097         
21098         if(this.calendarWeeks){
21099             dow.cn.push({
21100                 tag: 'th',
21101                 cls: 'cw',
21102                 html: '&nbsp;'
21103             })
21104         }
21105         
21106         while (dowCnt < this.weekStart + 7) {
21107             dow.cn.push({
21108                 tag: 'th',
21109                 cls: 'dow',
21110                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21111             });
21112         }
21113         
21114         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21115     },
21116     
21117     fillMonths: function()
21118     {    
21119         var i = 0;
21120         var months = this.picker().select('>.datepicker-months td', true).first();
21121         
21122         months.dom.innerHTML = '';
21123         
21124         while (i < 12) {
21125             var month = {
21126                 tag: 'span',
21127                 cls: 'month',
21128                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21129             };
21130             
21131             months.createChild(month);
21132         }
21133         
21134     },
21135     
21136     update: function()
21137     {
21138         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;
21139         
21140         if (this.date < this.startDate) {
21141             this.viewDate = new Date(this.startDate);
21142         } else if (this.date > this.endDate) {
21143             this.viewDate = new Date(this.endDate);
21144         } else {
21145             this.viewDate = new Date(this.date);
21146         }
21147         
21148         this.fill();
21149     },
21150     
21151     fill: function() 
21152     {
21153         var d = new Date(this.viewDate),
21154                 year = d.getUTCFullYear(),
21155                 month = d.getUTCMonth(),
21156                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21157                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21158                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21159                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21160                 currentDate = this.date && this.date.valueOf(),
21161                 today = this.UTCToday();
21162         
21163         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21164         
21165 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21166         
21167 //        this.picker.select('>tfoot th.today').
21168 //                                              .text(dates[this.language].today)
21169 //                                              .toggle(this.todayBtn !== false);
21170     
21171         this.updateNavArrows();
21172         this.fillMonths();
21173                                                 
21174         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21175         
21176         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21177          
21178         prevMonth.setUTCDate(day);
21179         
21180         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21181         
21182         var nextMonth = new Date(prevMonth);
21183         
21184         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21185         
21186         nextMonth = nextMonth.valueOf();
21187         
21188         var fillMonths = false;
21189         
21190         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21191         
21192         while(prevMonth.valueOf() <= nextMonth) {
21193             var clsName = '';
21194             
21195             if (prevMonth.getUTCDay() === this.weekStart) {
21196                 if(fillMonths){
21197                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21198                 }
21199                     
21200                 fillMonths = {
21201                     tag: 'tr',
21202                     cn: []
21203                 };
21204                 
21205                 if(this.calendarWeeks){
21206                     // ISO 8601: First week contains first thursday.
21207                     // ISO also states week starts on Monday, but we can be more abstract here.
21208                     var
21209                     // Start of current week: based on weekstart/current date
21210                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21211                     // Thursday of this week
21212                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21213                     // First Thursday of year, year from thursday
21214                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21215                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21216                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21217                     
21218                     fillMonths.cn.push({
21219                         tag: 'td',
21220                         cls: 'cw',
21221                         html: calWeek
21222                     });
21223                 }
21224             }
21225             
21226             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21227                 clsName += ' old';
21228             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21229                 clsName += ' new';
21230             }
21231             if (this.todayHighlight &&
21232                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21233                 prevMonth.getUTCMonth() == today.getMonth() &&
21234                 prevMonth.getUTCDate() == today.getDate()) {
21235                 clsName += ' today';
21236             }
21237             
21238             if (currentDate && prevMonth.valueOf() === currentDate) {
21239                 clsName += ' active';
21240             }
21241             
21242             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21243                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21244                     clsName += ' disabled';
21245             }
21246             
21247             fillMonths.cn.push({
21248                 tag: 'td',
21249                 cls: 'day ' + clsName,
21250                 html: prevMonth.getDate()
21251             });
21252             
21253             prevMonth.setDate(prevMonth.getDate()+1);
21254         }
21255           
21256         var currentYear = this.date && this.date.getUTCFullYear();
21257         var currentMonth = this.date && this.date.getUTCMonth();
21258         
21259         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21260         
21261         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21262             v.removeClass('active');
21263             
21264             if(currentYear === year && k === currentMonth){
21265                 v.addClass('active');
21266             }
21267             
21268             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21269                 v.addClass('disabled');
21270             }
21271             
21272         });
21273         
21274         
21275         year = parseInt(year/10, 10) * 10;
21276         
21277         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21278         
21279         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21280         
21281         year -= 1;
21282         for (var i = -1; i < 11; i++) {
21283             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21284                 tag: 'span',
21285                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21286                 html: year
21287             });
21288             
21289             year += 1;
21290         }
21291     },
21292     
21293     showMode: function(dir) 
21294     {
21295         if (dir) {
21296             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21297         }
21298         
21299         Roo.each(this.picker().select('>div',true).elements, function(v){
21300             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21301             v.hide();
21302         });
21303         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21304     },
21305     
21306     place: function()
21307     {
21308         if(this.isInline) {
21309             return;
21310         }
21311         
21312         this.picker().removeClass(['bottom', 'top']);
21313         
21314         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21315             /*
21316              * place to the top of element!
21317              *
21318              */
21319             
21320             this.picker().addClass('top');
21321             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21322             
21323             return;
21324         }
21325         
21326         this.picker().addClass('bottom');
21327         
21328         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21329     },
21330     
21331     parseDate : function(value)
21332     {
21333         if(!value || value instanceof Date){
21334             return value;
21335         }
21336         var v = Date.parseDate(value, this.format);
21337         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21338             v = Date.parseDate(value, 'Y-m-d');
21339         }
21340         if(!v && this.altFormats){
21341             if(!this.altFormatsArray){
21342                 this.altFormatsArray = this.altFormats.split("|");
21343             }
21344             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21345                 v = Date.parseDate(value, this.altFormatsArray[i]);
21346             }
21347         }
21348         return v;
21349     },
21350     
21351     formatDate : function(date, fmt)
21352     {   
21353         return (!date || !(date instanceof Date)) ?
21354         date : date.dateFormat(fmt || this.format);
21355     },
21356     
21357     onFocus : function()
21358     {
21359         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21360         this.showPopup();
21361     },
21362     
21363     onBlur : function()
21364     {
21365         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21366         
21367         var d = this.inputEl().getValue();
21368         
21369         this.setValue(d);
21370                 
21371         this.hidePopup();
21372     },
21373     
21374     showPopup : function()
21375     {
21376         this.picker().show();
21377         this.update();
21378         this.place();
21379         
21380         this.fireEvent('showpopup', this, this.date);
21381     },
21382     
21383     hidePopup : function()
21384     {
21385         if(this.isInline) {
21386             return;
21387         }
21388         this.picker().hide();
21389         this.viewMode = this.startViewMode;
21390         this.showMode();
21391         
21392         this.fireEvent('hidepopup', this, this.date);
21393         
21394     },
21395     
21396     onMousedown: function(e)
21397     {
21398         e.stopPropagation();
21399         e.preventDefault();
21400     },
21401     
21402     keyup: function(e)
21403     {
21404         Roo.bootstrap.DateField.superclass.keyup.call(this);
21405         this.update();
21406     },
21407
21408     setValue: function(v)
21409     {
21410         if(this.fireEvent('beforeselect', this, v) !== false){
21411             var d = new Date(this.parseDate(v) ).clearTime();
21412         
21413             if(isNaN(d.getTime())){
21414                 this.date = this.viewDate = '';
21415                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21416                 return;
21417             }
21418
21419             v = this.formatDate(d);
21420
21421             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21422
21423             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21424
21425             this.update();
21426
21427             this.fireEvent('select', this, this.date);
21428         }
21429     },
21430     
21431     getValue: function()
21432     {
21433         return this.formatDate(this.date);
21434     },
21435     
21436     fireKey: function(e)
21437     {
21438         if (!this.picker().isVisible()){
21439             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21440                 this.showPopup();
21441             }
21442             return;
21443         }
21444         
21445         var dateChanged = false,
21446         dir, day, month,
21447         newDate, newViewDate;
21448         
21449         switch(e.keyCode){
21450             case 27: // escape
21451                 this.hidePopup();
21452                 e.preventDefault();
21453                 break;
21454             case 37: // left
21455             case 39: // right
21456                 if (!this.keyboardNavigation) {
21457                     break;
21458                 }
21459                 dir = e.keyCode == 37 ? -1 : 1;
21460                 
21461                 if (e.ctrlKey){
21462                     newDate = this.moveYear(this.date, dir);
21463                     newViewDate = this.moveYear(this.viewDate, dir);
21464                 } else if (e.shiftKey){
21465                     newDate = this.moveMonth(this.date, dir);
21466                     newViewDate = this.moveMonth(this.viewDate, dir);
21467                 } else {
21468                     newDate = new Date(this.date);
21469                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21470                     newViewDate = new Date(this.viewDate);
21471                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21472                 }
21473                 if (this.dateWithinRange(newDate)){
21474                     this.date = newDate;
21475                     this.viewDate = newViewDate;
21476                     this.setValue(this.formatDate(this.date));
21477 //                    this.update();
21478                     e.preventDefault();
21479                     dateChanged = true;
21480                 }
21481                 break;
21482             case 38: // up
21483             case 40: // down
21484                 if (!this.keyboardNavigation) {
21485                     break;
21486                 }
21487                 dir = e.keyCode == 38 ? -1 : 1;
21488                 if (e.ctrlKey){
21489                     newDate = this.moveYear(this.date, dir);
21490                     newViewDate = this.moveYear(this.viewDate, dir);
21491                 } else if (e.shiftKey){
21492                     newDate = this.moveMonth(this.date, dir);
21493                     newViewDate = this.moveMonth(this.viewDate, dir);
21494                 } else {
21495                     newDate = new Date(this.date);
21496                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21497                     newViewDate = new Date(this.viewDate);
21498                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21499                 }
21500                 if (this.dateWithinRange(newDate)){
21501                     this.date = newDate;
21502                     this.viewDate = newViewDate;
21503                     this.setValue(this.formatDate(this.date));
21504 //                    this.update();
21505                     e.preventDefault();
21506                     dateChanged = true;
21507                 }
21508                 break;
21509             case 13: // enter
21510                 this.setValue(this.formatDate(this.date));
21511                 this.hidePopup();
21512                 e.preventDefault();
21513                 break;
21514             case 9: // tab
21515                 this.setValue(this.formatDate(this.date));
21516                 this.hidePopup();
21517                 break;
21518             case 16: // shift
21519             case 17: // ctrl
21520             case 18: // alt
21521                 break;
21522             default :
21523                 this.hidePopup();
21524                 
21525         }
21526     },
21527     
21528     
21529     onClick: function(e) 
21530     {
21531         e.stopPropagation();
21532         e.preventDefault();
21533         
21534         var target = e.getTarget();
21535         
21536         if(target.nodeName.toLowerCase() === 'i'){
21537             target = Roo.get(target).dom.parentNode;
21538         }
21539         
21540         var nodeName = target.nodeName;
21541         var className = target.className;
21542         var html = target.innerHTML;
21543         //Roo.log(nodeName);
21544         
21545         switch(nodeName.toLowerCase()) {
21546             case 'th':
21547                 switch(className) {
21548                     case 'switch':
21549                         this.showMode(1);
21550                         break;
21551                     case 'prev':
21552                     case 'next':
21553                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21554                         switch(this.viewMode){
21555                                 case 0:
21556                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21557                                         break;
21558                                 case 1:
21559                                 case 2:
21560                                         this.viewDate = this.moveYear(this.viewDate, dir);
21561                                         break;
21562                         }
21563                         this.fill();
21564                         break;
21565                     case 'today':
21566                         var date = new Date();
21567                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21568 //                        this.fill()
21569                         this.setValue(this.formatDate(this.date));
21570                         
21571                         this.hidePopup();
21572                         break;
21573                 }
21574                 break;
21575             case 'span':
21576                 if (className.indexOf('disabled') < 0) {
21577                     this.viewDate.setUTCDate(1);
21578                     if (className.indexOf('month') > -1) {
21579                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21580                     } else {
21581                         var year = parseInt(html, 10) || 0;
21582                         this.viewDate.setUTCFullYear(year);
21583                         
21584                     }
21585                     
21586                     if(this.singleMode){
21587                         this.setValue(this.formatDate(this.viewDate));
21588                         this.hidePopup();
21589                         return;
21590                     }
21591                     
21592                     this.showMode(-1);
21593                     this.fill();
21594                 }
21595                 break;
21596                 
21597             case 'td':
21598                 //Roo.log(className);
21599                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21600                     var day = parseInt(html, 10) || 1;
21601                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
21602                         month = (this.viewDate || new Date()).getUTCMonth();
21603
21604                     if (className.indexOf('old') > -1) {
21605                         if(month === 0 ){
21606                             month = 11;
21607                             year -= 1;
21608                         }else{
21609                             month -= 1;
21610                         }
21611                     } else if (className.indexOf('new') > -1) {
21612                         if (month == 11) {
21613                             month = 0;
21614                             year += 1;
21615                         } else {
21616                             month += 1;
21617                         }
21618                     }
21619                     //Roo.log([year,month,day]);
21620                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21621                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21622 //                    this.fill();
21623                     //Roo.log(this.formatDate(this.date));
21624                     this.setValue(this.formatDate(this.date));
21625                     this.hidePopup();
21626                 }
21627                 break;
21628         }
21629     },
21630     
21631     setStartDate: function(startDate)
21632     {
21633         this.startDate = startDate || -Infinity;
21634         if (this.startDate !== -Infinity) {
21635             this.startDate = this.parseDate(this.startDate);
21636         }
21637         this.update();
21638         this.updateNavArrows();
21639     },
21640
21641     setEndDate: function(endDate)
21642     {
21643         this.endDate = endDate || Infinity;
21644         if (this.endDate !== Infinity) {
21645             this.endDate = this.parseDate(this.endDate);
21646         }
21647         this.update();
21648         this.updateNavArrows();
21649     },
21650     
21651     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21652     {
21653         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21654         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21655             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21656         }
21657         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21658             return parseInt(d, 10);
21659         });
21660         this.update();
21661         this.updateNavArrows();
21662     },
21663     
21664     updateNavArrows: function() 
21665     {
21666         if(this.singleMode){
21667             return;
21668         }
21669         
21670         var d = new Date(this.viewDate),
21671         year = d.getUTCFullYear(),
21672         month = d.getUTCMonth();
21673         
21674         Roo.each(this.picker().select('.prev', true).elements, function(v){
21675             v.show();
21676             switch (this.viewMode) {
21677                 case 0:
21678
21679                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21680                         v.hide();
21681                     }
21682                     break;
21683                 case 1:
21684                 case 2:
21685                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21686                         v.hide();
21687                     }
21688                     break;
21689             }
21690         });
21691         
21692         Roo.each(this.picker().select('.next', true).elements, function(v){
21693             v.show();
21694             switch (this.viewMode) {
21695                 case 0:
21696
21697                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21698                         v.hide();
21699                     }
21700                     break;
21701                 case 1:
21702                 case 2:
21703                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21704                         v.hide();
21705                     }
21706                     break;
21707             }
21708         })
21709     },
21710     
21711     moveMonth: function(date, dir)
21712     {
21713         if (!dir) {
21714             return date;
21715         }
21716         var new_date = new Date(date.valueOf()),
21717         day = new_date.getUTCDate(),
21718         month = new_date.getUTCMonth(),
21719         mag = Math.abs(dir),
21720         new_month, test;
21721         dir = dir > 0 ? 1 : -1;
21722         if (mag == 1){
21723             test = dir == -1
21724             // If going back one month, make sure month is not current month
21725             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21726             ? function(){
21727                 return new_date.getUTCMonth() == month;
21728             }
21729             // If going forward one month, make sure month is as expected
21730             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21731             : function(){
21732                 return new_date.getUTCMonth() != new_month;
21733             };
21734             new_month = month + dir;
21735             new_date.setUTCMonth(new_month);
21736             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21737             if (new_month < 0 || new_month > 11) {
21738                 new_month = (new_month + 12) % 12;
21739             }
21740         } else {
21741             // For magnitudes >1, move one month at a time...
21742             for (var i=0; i<mag; i++) {
21743                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21744                 new_date = this.moveMonth(new_date, dir);
21745             }
21746             // ...then reset the day, keeping it in the new month
21747             new_month = new_date.getUTCMonth();
21748             new_date.setUTCDate(day);
21749             test = function(){
21750                 return new_month != new_date.getUTCMonth();
21751             };
21752         }
21753         // Common date-resetting loop -- if date is beyond end of month, make it
21754         // end of month
21755         while (test()){
21756             new_date.setUTCDate(--day);
21757             new_date.setUTCMonth(new_month);
21758         }
21759         return new_date;
21760     },
21761
21762     moveYear: function(date, dir)
21763     {
21764         return this.moveMonth(date, dir*12);
21765     },
21766
21767     dateWithinRange: function(date)
21768     {
21769         return date >= this.startDate && date <= this.endDate;
21770     },
21771
21772     
21773     remove: function() 
21774     {
21775         this.picker().remove();
21776     },
21777     
21778     validateValue : function(value)
21779     {
21780         if(this.getVisibilityEl().hasClass('hidden')){
21781             return true;
21782         }
21783         
21784         if(value.length < 1)  {
21785             if(this.allowBlank){
21786                 return true;
21787             }
21788             return false;
21789         }
21790         
21791         if(value.length < this.minLength){
21792             return false;
21793         }
21794         if(value.length > this.maxLength){
21795             return false;
21796         }
21797         if(this.vtype){
21798             var vt = Roo.form.VTypes;
21799             if(!vt[this.vtype](value, this)){
21800                 return false;
21801             }
21802         }
21803         if(typeof this.validator == "function"){
21804             var msg = this.validator(value);
21805             if(msg !== true){
21806                 return false;
21807             }
21808         }
21809         
21810         if(this.regex && !this.regex.test(value)){
21811             return false;
21812         }
21813         
21814         if(typeof(this.parseDate(value)) == 'undefined'){
21815             return false;
21816         }
21817         
21818         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21819             return false;
21820         }      
21821         
21822         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21823             return false;
21824         } 
21825         
21826         
21827         return true;
21828     },
21829     
21830     reset : function()
21831     {
21832         this.date = this.viewDate = '';
21833         
21834         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21835     }
21836    
21837 });
21838
21839 Roo.apply(Roo.bootstrap.DateField,  {
21840     
21841     head : {
21842         tag: 'thead',
21843         cn: [
21844         {
21845             tag: 'tr',
21846             cn: [
21847             {
21848                 tag: 'th',
21849                 cls: 'prev',
21850                 html: '<i class="fa fa-arrow-left"/>'
21851             },
21852             {
21853                 tag: 'th',
21854                 cls: 'switch',
21855                 colspan: '5'
21856             },
21857             {
21858                 tag: 'th',
21859                 cls: 'next',
21860                 html: '<i class="fa fa-arrow-right"/>'
21861             }
21862
21863             ]
21864         }
21865         ]
21866     },
21867     
21868     content : {
21869         tag: 'tbody',
21870         cn: [
21871         {
21872             tag: 'tr',
21873             cn: [
21874             {
21875                 tag: 'td',
21876                 colspan: '7'
21877             }
21878             ]
21879         }
21880         ]
21881     },
21882     
21883     footer : {
21884         tag: 'tfoot',
21885         cn: [
21886         {
21887             tag: 'tr',
21888             cn: [
21889             {
21890                 tag: 'th',
21891                 colspan: '7',
21892                 cls: 'today'
21893             }
21894                     
21895             ]
21896         }
21897         ]
21898     },
21899     
21900     dates:{
21901         en: {
21902             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21903             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21904             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21905             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21906             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21907             today: "Today"
21908         }
21909     },
21910     
21911     modes: [
21912     {
21913         clsName: 'days',
21914         navFnc: 'Month',
21915         navStep: 1
21916     },
21917     {
21918         clsName: 'months',
21919         navFnc: 'FullYear',
21920         navStep: 1
21921     },
21922     {
21923         clsName: 'years',
21924         navFnc: 'FullYear',
21925         navStep: 10
21926     }]
21927 });
21928
21929 Roo.apply(Roo.bootstrap.DateField,  {
21930   
21931     template : {
21932         tag: 'div',
21933         cls: 'datepicker dropdown-menu roo-dynamic shadow',
21934         cn: [
21935         {
21936             tag: 'div',
21937             cls: 'datepicker-days',
21938             cn: [
21939             {
21940                 tag: 'table',
21941                 cls: 'table-condensed',
21942                 cn:[
21943                 Roo.bootstrap.DateField.head,
21944                 {
21945                     tag: 'tbody'
21946                 },
21947                 Roo.bootstrap.DateField.footer
21948                 ]
21949             }
21950             ]
21951         },
21952         {
21953             tag: 'div',
21954             cls: 'datepicker-months',
21955             cn: [
21956             {
21957                 tag: 'table',
21958                 cls: 'table-condensed',
21959                 cn:[
21960                 Roo.bootstrap.DateField.head,
21961                 Roo.bootstrap.DateField.content,
21962                 Roo.bootstrap.DateField.footer
21963                 ]
21964             }
21965             ]
21966         },
21967         {
21968             tag: 'div',
21969             cls: 'datepicker-years',
21970             cn: [
21971             {
21972                 tag: 'table',
21973                 cls: 'table-condensed',
21974                 cn:[
21975                 Roo.bootstrap.DateField.head,
21976                 Roo.bootstrap.DateField.content,
21977                 Roo.bootstrap.DateField.footer
21978                 ]
21979             }
21980             ]
21981         }
21982         ]
21983     }
21984 });
21985
21986  
21987
21988  /*
21989  * - LGPL
21990  *
21991  * TimeField
21992  * 
21993  */
21994
21995 /**
21996  * @class Roo.bootstrap.TimeField
21997  * @extends Roo.bootstrap.Input
21998  * Bootstrap DateField class
21999  * 
22000  * 
22001  * @constructor
22002  * Create a new TimeField
22003  * @param {Object} config The config object
22004  */
22005
22006 Roo.bootstrap.TimeField = function(config){
22007     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22008     this.addEvents({
22009             /**
22010              * @event show
22011              * Fires when this field show.
22012              * @param {Roo.bootstrap.DateField} thisthis
22013              * @param {Mixed} date The date value
22014              */
22015             show : true,
22016             /**
22017              * @event show
22018              * Fires when this field hide.
22019              * @param {Roo.bootstrap.DateField} this
22020              * @param {Mixed} date The date value
22021              */
22022             hide : true,
22023             /**
22024              * @event select
22025              * Fires when select a date.
22026              * @param {Roo.bootstrap.DateField} this
22027              * @param {Mixed} date The date value
22028              */
22029             select : true
22030         });
22031 };
22032
22033 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
22034     
22035     /**
22036      * @cfg {String} format
22037      * The default time format string which can be overriden for localization support.  The format must be
22038      * valid according to {@link Date#parseDate} (defaults to 'H:i').
22039      */
22040     format : "H:i",
22041
22042     getAutoCreate : function()
22043     {
22044         this.after = '<i class="fa far fa-clock"></i>';
22045         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22046         
22047          
22048     },
22049     onRender: function(ct, position)
22050     {
22051         
22052         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22053                 
22054         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22055         
22056         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22057         
22058         this.pop = this.picker().select('>.datepicker-time',true).first();
22059         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22060         
22061         this.picker().on('mousedown', this.onMousedown, this);
22062         this.picker().on('click', this.onClick, this);
22063         
22064         this.picker().addClass('datepicker-dropdown');
22065     
22066         this.fillTime();
22067         this.update();
22068             
22069         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22070         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22071         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22072         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22073         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22074         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22075
22076     },
22077     
22078     fireKey: function(e){
22079         if (!this.picker().isVisible()){
22080             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22081                 this.show();
22082             }
22083             return;
22084         }
22085
22086         e.preventDefault();
22087         
22088         switch(e.keyCode){
22089             case 27: // escape
22090                 this.hide();
22091                 break;
22092             case 37: // left
22093             case 39: // right
22094                 this.onTogglePeriod();
22095                 break;
22096             case 38: // up
22097                 this.onIncrementMinutes();
22098                 break;
22099             case 40: // down
22100                 this.onDecrementMinutes();
22101                 break;
22102             case 13: // enter
22103             case 9: // tab
22104                 this.setTime();
22105                 break;
22106         }
22107     },
22108     
22109     onClick: function(e) {
22110         e.stopPropagation();
22111         e.preventDefault();
22112     },
22113     
22114     picker : function()
22115     {
22116         return this.pickerEl;
22117     },
22118     
22119     fillTime: function()
22120     {    
22121         var time = this.pop.select('tbody', true).first();
22122         
22123         time.dom.innerHTML = '';
22124         
22125         time.createChild({
22126             tag: 'tr',
22127             cn: [
22128                 {
22129                     tag: 'td',
22130                     cn: [
22131                         {
22132                             tag: 'a',
22133                             href: '#',
22134                             cls: 'btn',
22135                             cn: [
22136                                 {
22137                                     tag: 'i',
22138                                     cls: 'hours-up fa fas fa-chevron-up'
22139                                 }
22140                             ]
22141                         } 
22142                     ]
22143                 },
22144                 {
22145                     tag: 'td',
22146                     cls: 'separator'
22147                 },
22148                 {
22149                     tag: 'td',
22150                     cn: [
22151                         {
22152                             tag: 'a',
22153                             href: '#',
22154                             cls: 'btn',
22155                             cn: [
22156                                 {
22157                                     tag: 'i',
22158                                     cls: 'minutes-up fa fas fa-chevron-up'
22159                                 }
22160                             ]
22161                         }
22162                     ]
22163                 },
22164                 {
22165                     tag: 'td',
22166                     cls: 'separator'
22167                 }
22168             ]
22169         });
22170         
22171         time.createChild({
22172             tag: 'tr',
22173             cn: [
22174                 {
22175                     tag: 'td',
22176                     cn: [
22177                         {
22178                             tag: 'span',
22179                             cls: 'timepicker-hour',
22180                             html: '00'
22181                         }  
22182                     ]
22183                 },
22184                 {
22185                     tag: 'td',
22186                     cls: 'separator',
22187                     html: ':'
22188                 },
22189                 {
22190                     tag: 'td',
22191                     cn: [
22192                         {
22193                             tag: 'span',
22194                             cls: 'timepicker-minute',
22195                             html: '00'
22196                         }  
22197                     ]
22198                 },
22199                 {
22200                     tag: 'td',
22201                     cls: 'separator'
22202                 },
22203                 {
22204                     tag: 'td',
22205                     cn: [
22206                         {
22207                             tag: 'button',
22208                             type: 'button',
22209                             cls: 'btn btn-primary period',
22210                             html: 'AM'
22211                             
22212                         }
22213                     ]
22214                 }
22215             ]
22216         });
22217         
22218         time.createChild({
22219             tag: 'tr',
22220             cn: [
22221                 {
22222                     tag: 'td',
22223                     cn: [
22224                         {
22225                             tag: 'a',
22226                             href: '#',
22227                             cls: 'btn',
22228                             cn: [
22229                                 {
22230                                     tag: 'span',
22231                                     cls: 'hours-down fa fas fa-chevron-down'
22232                                 }
22233                             ]
22234                         }
22235                     ]
22236                 },
22237                 {
22238                     tag: 'td',
22239                     cls: 'separator'
22240                 },
22241                 {
22242                     tag: 'td',
22243                     cn: [
22244                         {
22245                             tag: 'a',
22246                             href: '#',
22247                             cls: 'btn',
22248                             cn: [
22249                                 {
22250                                     tag: 'span',
22251                                     cls: 'minutes-down fa fas fa-chevron-down'
22252                                 }
22253                             ]
22254                         }
22255                     ]
22256                 },
22257                 {
22258                     tag: 'td',
22259                     cls: 'separator'
22260                 }
22261             ]
22262         });
22263         
22264     },
22265     
22266     update: function()
22267     {
22268         
22269         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22270         
22271         this.fill();
22272     },
22273     
22274     fill: function() 
22275     {
22276         var hours = this.time.getHours();
22277         var minutes = this.time.getMinutes();
22278         var period = 'AM';
22279         
22280         if(hours > 11){
22281             period = 'PM';
22282         }
22283         
22284         if(hours == 0){
22285             hours = 12;
22286         }
22287         
22288         
22289         if(hours > 12){
22290             hours = hours - 12;
22291         }
22292         
22293         if(hours < 10){
22294             hours = '0' + hours;
22295         }
22296         
22297         if(minutes < 10){
22298             minutes = '0' + minutes;
22299         }
22300         
22301         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22302         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22303         this.pop.select('button', true).first().dom.innerHTML = period;
22304         
22305     },
22306     
22307     place: function()
22308     {   
22309         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22310         
22311         var cls = ['bottom'];
22312         
22313         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22314             cls.pop();
22315             cls.push('top');
22316         }
22317         
22318         cls.push('right');
22319         
22320         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22321             cls.pop();
22322             cls.push('left');
22323         }
22324         //this.picker().setXY(20000,20000);
22325         this.picker().addClass(cls.join('-'));
22326         
22327         var _this = this;
22328         
22329         Roo.each(cls, function(c){
22330             if(c == 'bottom'){
22331                 (function() {
22332                  //  
22333                 }).defer(200);
22334                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
22335                 //_this.picker().setTop(_this.inputEl().getHeight());
22336                 return;
22337             }
22338             if(c == 'top'){
22339                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
22340                 
22341                 //_this.picker().setTop(0 - _this.picker().getHeight());
22342                 return;
22343             }
22344             /*
22345             if(c == 'left'){
22346                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22347                 return;
22348             }
22349             if(c == 'right'){
22350                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22351                 return;
22352             }
22353             */
22354         });
22355         
22356     },
22357   
22358     onFocus : function()
22359     {
22360         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22361         this.show();
22362     },
22363     
22364     onBlur : function()
22365     {
22366         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22367         this.hide();
22368     },
22369     
22370     show : function()
22371     {
22372         this.picker().show();
22373         this.pop.show();
22374         this.update();
22375         this.place();
22376         
22377         this.fireEvent('show', this, this.date);
22378     },
22379     
22380     hide : function()
22381     {
22382         this.picker().hide();
22383         this.pop.hide();
22384         
22385         this.fireEvent('hide', this, this.date);
22386     },
22387     
22388     setTime : function()
22389     {
22390         this.hide();
22391         this.setValue(this.time.format(this.format));
22392         
22393         this.fireEvent('select', this, this.date);
22394         
22395         
22396     },
22397     
22398     onMousedown: function(e){
22399         e.stopPropagation();
22400         e.preventDefault();
22401     },
22402     
22403     onIncrementHours: function()
22404     {
22405         Roo.log('onIncrementHours');
22406         this.time = this.time.add(Date.HOUR, 1);
22407         this.update();
22408         
22409     },
22410     
22411     onDecrementHours: function()
22412     {
22413         Roo.log('onDecrementHours');
22414         this.time = this.time.add(Date.HOUR, -1);
22415         this.update();
22416     },
22417     
22418     onIncrementMinutes: function()
22419     {
22420         Roo.log('onIncrementMinutes');
22421         this.time = this.time.add(Date.MINUTE, 1);
22422         this.update();
22423     },
22424     
22425     onDecrementMinutes: function()
22426     {
22427         Roo.log('onDecrementMinutes');
22428         this.time = this.time.add(Date.MINUTE, -1);
22429         this.update();
22430     },
22431     
22432     onTogglePeriod: function()
22433     {
22434         Roo.log('onTogglePeriod');
22435         this.time = this.time.add(Date.HOUR, 12);
22436         this.update();
22437     }
22438     
22439    
22440 });
22441  
22442
22443 Roo.apply(Roo.bootstrap.TimeField,  {
22444   
22445     template : {
22446         tag: 'div',
22447         cls: 'datepicker dropdown-menu',
22448         cn: [
22449             {
22450                 tag: 'div',
22451                 cls: 'datepicker-time',
22452                 cn: [
22453                 {
22454                     tag: 'table',
22455                     cls: 'table-condensed',
22456                     cn:[
22457                         {
22458                             tag: 'tbody',
22459                             cn: [
22460                                 {
22461                                     tag: 'tr',
22462                                     cn: [
22463                                     {
22464                                         tag: 'td',
22465                                         colspan: '7'
22466                                     }
22467                                     ]
22468                                 }
22469                             ]
22470                         },
22471                         {
22472                             tag: 'tfoot',
22473                             cn: [
22474                                 {
22475                                     tag: 'tr',
22476                                     cn: [
22477                                     {
22478                                         tag: 'th',
22479                                         colspan: '7',
22480                                         cls: '',
22481                                         cn: [
22482                                             {
22483                                                 tag: 'button',
22484                                                 cls: 'btn btn-info ok',
22485                                                 html: 'OK'
22486                                             }
22487                                         ]
22488                                     }
22489                     
22490                                     ]
22491                                 }
22492                             ]
22493                         }
22494                     ]
22495                 }
22496                 ]
22497             }
22498         ]
22499     }
22500 });
22501
22502  
22503
22504  /*
22505  * - LGPL
22506  *
22507  * MonthField
22508  * 
22509  */
22510
22511 /**
22512  * @class Roo.bootstrap.MonthField
22513  * @extends Roo.bootstrap.Input
22514  * Bootstrap MonthField class
22515  * 
22516  * @cfg {String} language default en
22517  * 
22518  * @constructor
22519  * Create a new MonthField
22520  * @param {Object} config The config object
22521  */
22522
22523 Roo.bootstrap.MonthField = function(config){
22524     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22525     
22526     this.addEvents({
22527         /**
22528          * @event show
22529          * Fires when this field show.
22530          * @param {Roo.bootstrap.MonthField} this
22531          * @param {Mixed} date The date value
22532          */
22533         show : true,
22534         /**
22535          * @event show
22536          * Fires when this field hide.
22537          * @param {Roo.bootstrap.MonthField} this
22538          * @param {Mixed} date The date value
22539          */
22540         hide : true,
22541         /**
22542          * @event select
22543          * Fires when select a date.
22544          * @param {Roo.bootstrap.MonthField} this
22545          * @param {String} oldvalue The old value
22546          * @param {String} newvalue The new value
22547          */
22548         select : true
22549     });
22550 };
22551
22552 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22553     
22554     onRender: function(ct, position)
22555     {
22556         
22557         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22558         
22559         this.language = this.language || 'en';
22560         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22561         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22562         
22563         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22564         this.isInline = false;
22565         this.isInput = true;
22566         this.component = this.el.select('.add-on', true).first() || false;
22567         this.component = (this.component && this.component.length === 0) ? false : this.component;
22568         this.hasInput = this.component && this.inputEL().length;
22569         
22570         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22571         
22572         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22573         
22574         this.picker().on('mousedown', this.onMousedown, this);
22575         this.picker().on('click', this.onClick, this);
22576         
22577         this.picker().addClass('datepicker-dropdown');
22578         
22579         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22580             v.setStyle('width', '189px');
22581         });
22582         
22583         this.fillMonths();
22584         
22585         this.update();
22586         
22587         if(this.isInline) {
22588             this.show();
22589         }
22590         
22591     },
22592     
22593     setValue: function(v, suppressEvent)
22594     {   
22595         var o = this.getValue();
22596         
22597         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22598         
22599         this.update();
22600
22601         if(suppressEvent !== true){
22602             this.fireEvent('select', this, o, v);
22603         }
22604         
22605     },
22606     
22607     getValue: function()
22608     {
22609         return this.value;
22610     },
22611     
22612     onClick: function(e) 
22613     {
22614         e.stopPropagation();
22615         e.preventDefault();
22616         
22617         var target = e.getTarget();
22618         
22619         if(target.nodeName.toLowerCase() === 'i'){
22620             target = Roo.get(target).dom.parentNode;
22621         }
22622         
22623         var nodeName = target.nodeName;
22624         var className = target.className;
22625         var html = target.innerHTML;
22626         
22627         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22628             return;
22629         }
22630         
22631         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22632         
22633         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22634         
22635         this.hide();
22636                         
22637     },
22638     
22639     picker : function()
22640     {
22641         return this.pickerEl;
22642     },
22643     
22644     fillMonths: function()
22645     {    
22646         var i = 0;
22647         var months = this.picker().select('>.datepicker-months td', true).first();
22648         
22649         months.dom.innerHTML = '';
22650         
22651         while (i < 12) {
22652             var month = {
22653                 tag: 'span',
22654                 cls: 'month',
22655                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22656             };
22657             
22658             months.createChild(month);
22659         }
22660         
22661     },
22662     
22663     update: function()
22664     {
22665         var _this = this;
22666         
22667         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22668             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22669         }
22670         
22671         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22672             e.removeClass('active');
22673             
22674             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22675                 e.addClass('active');
22676             }
22677         })
22678     },
22679     
22680     place: function()
22681     {
22682         if(this.isInline) {
22683             return;
22684         }
22685         
22686         this.picker().removeClass(['bottom', 'top']);
22687         
22688         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22689             /*
22690              * place to the top of element!
22691              *
22692              */
22693             
22694             this.picker().addClass('top');
22695             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22696             
22697             return;
22698         }
22699         
22700         this.picker().addClass('bottom');
22701         
22702         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22703     },
22704     
22705     onFocus : function()
22706     {
22707         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22708         this.show();
22709     },
22710     
22711     onBlur : function()
22712     {
22713         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22714         
22715         var d = this.inputEl().getValue();
22716         
22717         this.setValue(d);
22718                 
22719         this.hide();
22720     },
22721     
22722     show : function()
22723     {
22724         this.picker().show();
22725         this.picker().select('>.datepicker-months', true).first().show();
22726         this.update();
22727         this.place();
22728         
22729         this.fireEvent('show', this, this.date);
22730     },
22731     
22732     hide : function()
22733     {
22734         if(this.isInline) {
22735             return;
22736         }
22737         this.picker().hide();
22738         this.fireEvent('hide', this, this.date);
22739         
22740     },
22741     
22742     onMousedown: function(e)
22743     {
22744         e.stopPropagation();
22745         e.preventDefault();
22746     },
22747     
22748     keyup: function(e)
22749     {
22750         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22751         this.update();
22752     },
22753
22754     fireKey: function(e)
22755     {
22756         if (!this.picker().isVisible()){
22757             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22758                 this.show();
22759             }
22760             return;
22761         }
22762         
22763         var dir;
22764         
22765         switch(e.keyCode){
22766             case 27: // escape
22767                 this.hide();
22768                 e.preventDefault();
22769                 break;
22770             case 37: // left
22771             case 39: // right
22772                 dir = e.keyCode == 37 ? -1 : 1;
22773                 
22774                 this.vIndex = this.vIndex + dir;
22775                 
22776                 if(this.vIndex < 0){
22777                     this.vIndex = 0;
22778                 }
22779                 
22780                 if(this.vIndex > 11){
22781                     this.vIndex = 11;
22782                 }
22783                 
22784                 if(isNaN(this.vIndex)){
22785                     this.vIndex = 0;
22786                 }
22787                 
22788                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22789                 
22790                 break;
22791             case 38: // up
22792             case 40: // down
22793                 
22794                 dir = e.keyCode == 38 ? -1 : 1;
22795                 
22796                 this.vIndex = this.vIndex + dir * 4;
22797                 
22798                 if(this.vIndex < 0){
22799                     this.vIndex = 0;
22800                 }
22801                 
22802                 if(this.vIndex > 11){
22803                     this.vIndex = 11;
22804                 }
22805                 
22806                 if(isNaN(this.vIndex)){
22807                     this.vIndex = 0;
22808                 }
22809                 
22810                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22811                 break;
22812                 
22813             case 13: // enter
22814                 
22815                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22816                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22817                 }
22818                 
22819                 this.hide();
22820                 e.preventDefault();
22821                 break;
22822             case 9: // tab
22823                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22824                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22825                 }
22826                 this.hide();
22827                 break;
22828             case 16: // shift
22829             case 17: // ctrl
22830             case 18: // alt
22831                 break;
22832             default :
22833                 this.hide();
22834                 
22835         }
22836     },
22837     
22838     remove: function() 
22839     {
22840         this.picker().remove();
22841     }
22842    
22843 });
22844
22845 Roo.apply(Roo.bootstrap.MonthField,  {
22846     
22847     content : {
22848         tag: 'tbody',
22849         cn: [
22850         {
22851             tag: 'tr',
22852             cn: [
22853             {
22854                 tag: 'td',
22855                 colspan: '7'
22856             }
22857             ]
22858         }
22859         ]
22860     },
22861     
22862     dates:{
22863         en: {
22864             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22865             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22866         }
22867     }
22868 });
22869
22870 Roo.apply(Roo.bootstrap.MonthField,  {
22871   
22872     template : {
22873         tag: 'div',
22874         cls: 'datepicker dropdown-menu roo-dynamic',
22875         cn: [
22876             {
22877                 tag: 'div',
22878                 cls: 'datepicker-months',
22879                 cn: [
22880                 {
22881                     tag: 'table',
22882                     cls: 'table-condensed',
22883                     cn:[
22884                         Roo.bootstrap.DateField.content
22885                     ]
22886                 }
22887                 ]
22888             }
22889         ]
22890     }
22891 });
22892
22893  
22894
22895  
22896  /*
22897  * - LGPL
22898  *
22899  * CheckBox
22900  * 
22901  */
22902
22903 /**
22904  * @class Roo.bootstrap.CheckBox
22905  * @extends Roo.bootstrap.Input
22906  * Bootstrap CheckBox class
22907  * 
22908  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22909  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22910  * @cfg {String} boxLabel The text that appears beside the checkbox
22911  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22912  * @cfg {Boolean} checked initnal the element
22913  * @cfg {Boolean} inline inline the element (default false)
22914  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22915  * @cfg {String} tooltip label tooltip
22916  * 
22917  * @constructor
22918  * Create a new CheckBox
22919  * @param {Object} config The config object
22920  */
22921
22922 Roo.bootstrap.CheckBox = function(config){
22923     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22924    
22925     this.addEvents({
22926         /**
22927         * @event check
22928         * Fires when the element is checked or unchecked.
22929         * @param {Roo.bootstrap.CheckBox} this This input
22930         * @param {Boolean} checked The new checked value
22931         */
22932        check : true,
22933        /**
22934         * @event click
22935         * Fires when the element is click.
22936         * @param {Roo.bootstrap.CheckBox} this This input
22937         */
22938        click : true
22939     });
22940     
22941 };
22942
22943 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
22944   
22945     inputType: 'checkbox',
22946     inputValue: 1,
22947     valueOff: 0,
22948     boxLabel: false,
22949     checked: false,
22950     weight : false,
22951     inline: false,
22952     tooltip : '',
22953     
22954     // checkbox success does not make any sense really.. 
22955     invalidClass : "",
22956     validClass : "",
22957     
22958     
22959     getAutoCreate : function()
22960     {
22961         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22962         
22963         var id = Roo.id();
22964         
22965         var cfg = {};
22966         
22967         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22968         
22969         if(this.inline){
22970             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
22971         }
22972         
22973         var input =  {
22974             tag: 'input',
22975             id : id,
22976             type : this.inputType,
22977             value : this.inputValue,
22978             cls : 'roo-' + this.inputType, //'form-box',
22979             placeholder : this.placeholder || ''
22980             
22981         };
22982         
22983         if(this.inputType != 'radio'){
22984             var hidden =  {
22985                 tag: 'input',
22986                 type : 'hidden',
22987                 cls : 'roo-hidden-value',
22988                 value : this.checked ? this.inputValue : this.valueOff
22989             };
22990         }
22991         
22992             
22993         if (this.weight) { // Validity check?
22994             cfg.cls += " " + this.inputType + "-" + this.weight;
22995         }
22996         
22997         if (this.disabled) {
22998             input.disabled=true;
22999         }
23000         
23001         if(this.checked){
23002             input.checked = this.checked;
23003         }
23004         
23005         if (this.name) {
23006             
23007             input.name = this.name;
23008             
23009             if(this.inputType != 'radio'){
23010                 hidden.name = this.name;
23011                 input.name = '_hidden_' + this.name;
23012             }
23013         }
23014         
23015         if (this.size) {
23016             input.cls += ' input-' + this.size;
23017         }
23018         
23019         var settings=this;
23020         
23021         ['xs','sm','md','lg'].map(function(size){
23022             if (settings[size]) {
23023                 cfg.cls += ' col-' + size + '-' + settings[size];
23024             }
23025         });
23026         
23027         var inputblock = input;
23028          
23029         if (this.before || this.after) {
23030             
23031             inputblock = {
23032                 cls : 'input-group',
23033                 cn :  [] 
23034             };
23035             
23036             if (this.before) {
23037                 inputblock.cn.push({
23038                     tag :'span',
23039                     cls : 'input-group-addon',
23040                     html : this.before
23041                 });
23042             }
23043             
23044             inputblock.cn.push(input);
23045             
23046             if(this.inputType != 'radio'){
23047                 inputblock.cn.push(hidden);
23048             }
23049             
23050             if (this.after) {
23051                 inputblock.cn.push({
23052                     tag :'span',
23053                     cls : 'input-group-addon',
23054                     html : this.after
23055                 });
23056             }
23057             
23058         }
23059         var boxLabelCfg = false;
23060         
23061         if(this.boxLabel){
23062            
23063             boxLabelCfg = {
23064                 tag: 'label',
23065                 //'for': id, // box label is handled by onclick - so no for...
23066                 cls: 'box-label',
23067                 html: this.boxLabel
23068             };
23069             if(this.tooltip){
23070                 boxLabelCfg.tooltip = this.tooltip;
23071             }
23072              
23073         }
23074         
23075         
23076         if (align ==='left' && this.fieldLabel.length) {
23077 //                Roo.log("left and has label");
23078             cfg.cn = [
23079                 {
23080                     tag: 'label',
23081                     'for' :  id,
23082                     cls : 'control-label',
23083                     html : this.fieldLabel
23084                 },
23085                 {
23086                     cls : "", 
23087                     cn: [
23088                         inputblock
23089                     ]
23090                 }
23091             ];
23092             
23093             if (boxLabelCfg) {
23094                 cfg.cn[1].cn.push(boxLabelCfg);
23095             }
23096             
23097             if(this.labelWidth > 12){
23098                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23099             }
23100             
23101             if(this.labelWidth < 13 && this.labelmd == 0){
23102                 this.labelmd = this.labelWidth;
23103             }
23104             
23105             if(this.labellg > 0){
23106                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23107                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23108             }
23109             
23110             if(this.labelmd > 0){
23111                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23112                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23113             }
23114             
23115             if(this.labelsm > 0){
23116                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23117                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23118             }
23119             
23120             if(this.labelxs > 0){
23121                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23122                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23123             }
23124             
23125         } else if ( this.fieldLabel.length) {
23126 //                Roo.log(" label");
23127                 cfg.cn = [
23128                    
23129                     {
23130                         tag: this.boxLabel ? 'span' : 'label',
23131                         'for': id,
23132                         cls: 'control-label box-input-label',
23133                         //cls : 'input-group-addon',
23134                         html : this.fieldLabel
23135                     },
23136                     
23137                     inputblock
23138                     
23139                 ];
23140                 if (boxLabelCfg) {
23141                     cfg.cn.push(boxLabelCfg);
23142                 }
23143
23144         } else {
23145             
23146 //                Roo.log(" no label && no align");
23147                 cfg.cn = [  inputblock ] ;
23148                 if (boxLabelCfg) {
23149                     cfg.cn.push(boxLabelCfg);
23150                 }
23151
23152                 
23153         }
23154         
23155        
23156         
23157         if(this.inputType != 'radio'){
23158             cfg.cn.push(hidden);
23159         }
23160         
23161         return cfg;
23162         
23163     },
23164     
23165     /**
23166      * return the real input element.
23167      */
23168     inputEl: function ()
23169     {
23170         return this.el.select('input.roo-' + this.inputType,true).first();
23171     },
23172     hiddenEl: function ()
23173     {
23174         return this.el.select('input.roo-hidden-value',true).first();
23175     },
23176     
23177     labelEl: function()
23178     {
23179         return this.el.select('label.control-label',true).first();
23180     },
23181     /* depricated... */
23182     
23183     label: function()
23184     {
23185         return this.labelEl();
23186     },
23187     
23188     boxLabelEl: function()
23189     {
23190         return this.el.select('label.box-label',true).first();
23191     },
23192     
23193     initEvents : function()
23194     {
23195 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23196         
23197         this.inputEl().on('click', this.onClick,  this);
23198         
23199         if (this.boxLabel) { 
23200             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23201         }
23202         
23203         this.startValue = this.getValue();
23204         
23205         if(this.groupId){
23206             Roo.bootstrap.CheckBox.register(this);
23207         }
23208     },
23209     
23210     onClick : function(e)
23211     {   
23212         if(this.fireEvent('click', this, e) !== false){
23213             this.setChecked(!this.checked);
23214         }
23215         
23216     },
23217     
23218     setChecked : function(state,suppressEvent)
23219     {
23220         this.startValue = this.getValue();
23221
23222         if(this.inputType == 'radio'){
23223             
23224             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23225                 e.dom.checked = false;
23226             });
23227             
23228             this.inputEl().dom.checked = true;
23229             
23230             this.inputEl().dom.value = this.inputValue;
23231             
23232             if(suppressEvent !== true){
23233                 this.fireEvent('check', this, true);
23234             }
23235             
23236             this.validate();
23237             
23238             return;
23239         }
23240         
23241         this.checked = state;
23242         
23243         this.inputEl().dom.checked = state;
23244         
23245         
23246         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23247         
23248         if(suppressEvent !== true){
23249             this.fireEvent('check', this, state);
23250         }
23251         
23252         this.validate();
23253     },
23254     
23255     getValue : function()
23256     {
23257         if(this.inputType == 'radio'){
23258             return this.getGroupValue();
23259         }
23260         
23261         return this.hiddenEl().dom.value;
23262         
23263     },
23264     
23265     getGroupValue : function()
23266     {
23267         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23268             return '';
23269         }
23270         
23271         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23272     },
23273     
23274     setValue : function(v,suppressEvent)
23275     {
23276         if(this.inputType == 'radio'){
23277             this.setGroupValue(v, suppressEvent);
23278             return;
23279         }
23280         
23281         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23282         
23283         this.validate();
23284     },
23285     
23286     setGroupValue : function(v, suppressEvent)
23287     {
23288         this.startValue = this.getValue();
23289         
23290         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23291             e.dom.checked = false;
23292             
23293             if(e.dom.value == v){
23294                 e.dom.checked = true;
23295             }
23296         });
23297         
23298         if(suppressEvent !== true){
23299             this.fireEvent('check', this, true);
23300         }
23301
23302         this.validate();
23303         
23304         return;
23305     },
23306     
23307     validate : function()
23308     {
23309         if(this.getVisibilityEl().hasClass('hidden')){
23310             return true;
23311         }
23312         
23313         if(
23314                 this.disabled || 
23315                 (this.inputType == 'radio' && this.validateRadio()) ||
23316                 (this.inputType == 'checkbox' && this.validateCheckbox())
23317         ){
23318             this.markValid();
23319             return true;
23320         }
23321         
23322         this.markInvalid();
23323         return false;
23324     },
23325     
23326     validateRadio : function()
23327     {
23328         if(this.getVisibilityEl().hasClass('hidden')){
23329             return true;
23330         }
23331         
23332         if(this.allowBlank){
23333             return true;
23334         }
23335         
23336         var valid = false;
23337         
23338         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23339             if(!e.dom.checked){
23340                 return;
23341             }
23342             
23343             valid = true;
23344             
23345             return false;
23346         });
23347         
23348         return valid;
23349     },
23350     
23351     validateCheckbox : function()
23352     {
23353         if(!this.groupId){
23354             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23355             //return (this.getValue() == this.inputValue) ? true : false;
23356         }
23357         
23358         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23359         
23360         if(!group){
23361             return false;
23362         }
23363         
23364         var r = false;
23365         
23366         for(var i in group){
23367             if(group[i].el.isVisible(true)){
23368                 r = false;
23369                 break;
23370             }
23371             
23372             r = true;
23373         }
23374         
23375         for(var i in group){
23376             if(r){
23377                 break;
23378             }
23379             
23380             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23381         }
23382         
23383         return r;
23384     },
23385     
23386     /**
23387      * Mark this field as valid
23388      */
23389     markValid : function()
23390     {
23391         var _this = this;
23392         
23393         this.fireEvent('valid', this);
23394         
23395         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23396         
23397         if(this.groupId){
23398             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23399         }
23400         
23401         if(label){
23402             label.markValid();
23403         }
23404
23405         if(this.inputType == 'radio'){
23406             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23407                 var fg = e.findParent('.form-group', false, true);
23408                 if (Roo.bootstrap.version == 3) {
23409                     fg.removeClass([_this.invalidClass, _this.validClass]);
23410                     fg.addClass(_this.validClass);
23411                 } else {
23412                     fg.removeClass(['is-valid', 'is-invalid']);
23413                     fg.addClass('is-valid');
23414                 }
23415             });
23416             
23417             return;
23418         }
23419
23420         if(!this.groupId){
23421             var fg = this.el.findParent('.form-group', false, true);
23422             if (Roo.bootstrap.version == 3) {
23423                 fg.removeClass([this.invalidClass, this.validClass]);
23424                 fg.addClass(this.validClass);
23425             } else {
23426                 fg.removeClass(['is-valid', 'is-invalid']);
23427                 fg.addClass('is-valid');
23428             }
23429             return;
23430         }
23431         
23432         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23433         
23434         if(!group){
23435             return;
23436         }
23437         
23438         for(var i in group){
23439             var fg = group[i].el.findParent('.form-group', false, true);
23440             if (Roo.bootstrap.version == 3) {
23441                 fg.removeClass([this.invalidClass, this.validClass]);
23442                 fg.addClass(this.validClass);
23443             } else {
23444                 fg.removeClass(['is-valid', 'is-invalid']);
23445                 fg.addClass('is-valid');
23446             }
23447         }
23448     },
23449     
23450      /**
23451      * Mark this field as invalid
23452      * @param {String} msg The validation message
23453      */
23454     markInvalid : function(msg)
23455     {
23456         if(this.allowBlank){
23457             return;
23458         }
23459         
23460         var _this = this;
23461         
23462         this.fireEvent('invalid', this, msg);
23463         
23464         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23465         
23466         if(this.groupId){
23467             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23468         }
23469         
23470         if(label){
23471             label.markInvalid();
23472         }
23473             
23474         if(this.inputType == 'radio'){
23475             
23476             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23477                 var fg = e.findParent('.form-group', false, true);
23478                 if (Roo.bootstrap.version == 3) {
23479                     fg.removeClass([_this.invalidClass, _this.validClass]);
23480                     fg.addClass(_this.invalidClass);
23481                 } else {
23482                     fg.removeClass(['is-invalid', 'is-valid']);
23483                     fg.addClass('is-invalid');
23484                 }
23485             });
23486             
23487             return;
23488         }
23489         
23490         if(!this.groupId){
23491             var fg = this.el.findParent('.form-group', false, true);
23492             if (Roo.bootstrap.version == 3) {
23493                 fg.removeClass([_this.invalidClass, _this.validClass]);
23494                 fg.addClass(_this.invalidClass);
23495             } else {
23496                 fg.removeClass(['is-invalid', 'is-valid']);
23497                 fg.addClass('is-invalid');
23498             }
23499             return;
23500         }
23501         
23502         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23503         
23504         if(!group){
23505             return;
23506         }
23507         
23508         for(var i in group){
23509             var fg = group[i].el.findParent('.form-group', false, true);
23510             if (Roo.bootstrap.version == 3) {
23511                 fg.removeClass([_this.invalidClass, _this.validClass]);
23512                 fg.addClass(_this.invalidClass);
23513             } else {
23514                 fg.removeClass(['is-invalid', 'is-valid']);
23515                 fg.addClass('is-invalid');
23516             }
23517         }
23518         
23519     },
23520     
23521     clearInvalid : function()
23522     {
23523         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23524         
23525         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23526         
23527         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23528         
23529         if (label && label.iconEl) {
23530             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23531             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23532         }
23533     },
23534     
23535     disable : function()
23536     {
23537         if(this.inputType != 'radio'){
23538             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23539             return;
23540         }
23541         
23542         var _this = this;
23543         
23544         if(this.rendered){
23545             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23546                 _this.getActionEl().addClass(this.disabledClass);
23547                 e.dom.disabled = true;
23548             });
23549         }
23550         
23551         this.disabled = true;
23552         this.fireEvent("disable", this);
23553         return this;
23554     },
23555
23556     enable : function()
23557     {
23558         if(this.inputType != 'radio'){
23559             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23560             return;
23561         }
23562         
23563         var _this = this;
23564         
23565         if(this.rendered){
23566             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23567                 _this.getActionEl().removeClass(this.disabledClass);
23568                 e.dom.disabled = false;
23569             });
23570         }
23571         
23572         this.disabled = false;
23573         this.fireEvent("enable", this);
23574         return this;
23575     },
23576     
23577     setBoxLabel : function(v)
23578     {
23579         this.boxLabel = v;
23580         
23581         if(this.rendered){
23582             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23583         }
23584     }
23585
23586 });
23587
23588 Roo.apply(Roo.bootstrap.CheckBox, {
23589     
23590     groups: {},
23591     
23592      /**
23593     * register a CheckBox Group
23594     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23595     */
23596     register : function(checkbox)
23597     {
23598         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23599             this.groups[checkbox.groupId] = {};
23600         }
23601         
23602         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23603             return;
23604         }
23605         
23606         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23607         
23608     },
23609     /**
23610     * fetch a CheckBox Group based on the group ID
23611     * @param {string} the group ID
23612     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23613     */
23614     get: function(groupId) {
23615         if (typeof(this.groups[groupId]) == 'undefined') {
23616             return false;
23617         }
23618         
23619         return this.groups[groupId] ;
23620     }
23621     
23622     
23623 });
23624 /*
23625  * - LGPL
23626  *
23627  * RadioItem
23628  * 
23629  */
23630
23631 /**
23632  * @class Roo.bootstrap.Radio
23633  * @extends Roo.bootstrap.Component
23634  * Bootstrap Radio class
23635  * @cfg {String} boxLabel - the label associated
23636  * @cfg {String} value - the value of radio
23637  * 
23638  * @constructor
23639  * Create a new Radio
23640  * @param {Object} config The config object
23641  */
23642 Roo.bootstrap.Radio = function(config){
23643     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23644     
23645 };
23646
23647 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23648     
23649     boxLabel : '',
23650     
23651     value : '',
23652     
23653     getAutoCreate : function()
23654     {
23655         var cfg = {
23656             tag : 'div',
23657             cls : 'form-group radio',
23658             cn : [
23659                 {
23660                     tag : 'label',
23661                     cls : 'box-label',
23662                     html : this.boxLabel
23663                 }
23664             ]
23665         };
23666         
23667         return cfg;
23668     },
23669     
23670     initEvents : function() 
23671     {
23672         this.parent().register(this);
23673         
23674         this.el.on('click', this.onClick, this);
23675         
23676     },
23677     
23678     onClick : function(e)
23679     {
23680         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23681             this.setChecked(true);
23682         }
23683     },
23684     
23685     setChecked : function(state, suppressEvent)
23686     {
23687         this.parent().setValue(this.value, suppressEvent);
23688         
23689     },
23690     
23691     setBoxLabel : function(v)
23692     {
23693         this.boxLabel = v;
23694         
23695         if(this.rendered){
23696             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23697         }
23698     }
23699     
23700 });
23701  
23702
23703  /*
23704  * - LGPL
23705  *
23706  * Input
23707  * 
23708  */
23709
23710 /**
23711  * @class Roo.bootstrap.SecurePass
23712  * @extends Roo.bootstrap.Input
23713  * Bootstrap SecurePass class
23714  *
23715  * 
23716  * @constructor
23717  * Create a new SecurePass
23718  * @param {Object} config The config object
23719  */
23720  
23721 Roo.bootstrap.SecurePass = function (config) {
23722     // these go here, so the translation tool can replace them..
23723     this.errors = {
23724         PwdEmpty: "Please type a password, and then retype it to confirm.",
23725         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23726         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23727         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23728         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23729         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23730         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23731         TooWeak: "Your password is Too Weak."
23732     },
23733     this.meterLabel = "Password strength:";
23734     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23735     this.meterClass = [
23736         "roo-password-meter-tooweak", 
23737         "roo-password-meter-weak", 
23738         "roo-password-meter-medium", 
23739         "roo-password-meter-strong", 
23740         "roo-password-meter-grey"
23741     ];
23742     
23743     this.errors = {};
23744     
23745     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23746 }
23747
23748 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23749     /**
23750      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23751      * {
23752      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23753      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23754      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23755      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23756      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23757      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23758      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23759      * })
23760      */
23761     // private
23762     
23763     meterWidth: 300,
23764     errorMsg :'',    
23765     errors: false,
23766     imageRoot: '/',
23767     /**
23768      * @cfg {String/Object} Label for the strength meter (defaults to
23769      * 'Password strength:')
23770      */
23771     // private
23772     meterLabel: '',
23773     /**
23774      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23775      * ['Weak', 'Medium', 'Strong'])
23776      */
23777     // private    
23778     pwdStrengths: false,    
23779     // private
23780     strength: 0,
23781     // private
23782     _lastPwd: null,
23783     // private
23784     kCapitalLetter: 0,
23785     kSmallLetter: 1,
23786     kDigit: 2,
23787     kPunctuation: 3,
23788     
23789     insecure: false,
23790     // private
23791     initEvents: function ()
23792     {
23793         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23794
23795         if (this.el.is('input[type=password]') && Roo.isSafari) {
23796             this.el.on('keydown', this.SafariOnKeyDown, this);
23797         }
23798
23799         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23800     },
23801     // private
23802     onRender: function (ct, position)
23803     {
23804         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23805         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23806         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23807
23808         this.trigger.createChild({
23809                    cn: [
23810                     {
23811                     //id: 'PwdMeter',
23812                     tag: 'div',
23813                     cls: 'roo-password-meter-grey col-xs-12',
23814                     style: {
23815                         //width: 0,
23816                         //width: this.meterWidth + 'px'                                                
23817                         }
23818                     },
23819                     {                            
23820                          cls: 'roo-password-meter-text'                          
23821                     }
23822                 ]            
23823         });
23824
23825          
23826         if (this.hideTrigger) {
23827             this.trigger.setDisplayed(false);
23828         }
23829         this.setSize(this.width || '', this.height || '');
23830     },
23831     // private
23832     onDestroy: function ()
23833     {
23834         if (this.trigger) {
23835             this.trigger.removeAllListeners();
23836             this.trigger.remove();
23837         }
23838         if (this.wrap) {
23839             this.wrap.remove();
23840         }
23841         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23842     },
23843     // private
23844     checkStrength: function ()
23845     {
23846         var pwd = this.inputEl().getValue();
23847         if (pwd == this._lastPwd) {
23848             return;
23849         }
23850
23851         var strength;
23852         if (this.ClientSideStrongPassword(pwd)) {
23853             strength = 3;
23854         } else if (this.ClientSideMediumPassword(pwd)) {
23855             strength = 2;
23856         } else if (this.ClientSideWeakPassword(pwd)) {
23857             strength = 1;
23858         } else {
23859             strength = 0;
23860         }
23861         
23862         Roo.log('strength1: ' + strength);
23863         
23864         //var pm = this.trigger.child('div/div/div').dom;
23865         var pm = this.trigger.child('div/div');
23866         pm.removeClass(this.meterClass);
23867         pm.addClass(this.meterClass[strength]);
23868                 
23869         
23870         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23871                 
23872         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23873         
23874         this._lastPwd = pwd;
23875     },
23876     reset: function ()
23877     {
23878         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23879         
23880         this._lastPwd = '';
23881         
23882         var pm = this.trigger.child('div/div');
23883         pm.removeClass(this.meterClass);
23884         pm.addClass('roo-password-meter-grey');        
23885         
23886         
23887         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23888         
23889         pt.innerHTML = '';
23890         this.inputEl().dom.type='password';
23891     },
23892     // private
23893     validateValue: function (value)
23894     {
23895         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23896             return false;
23897         }
23898         if (value.length == 0) {
23899             if (this.allowBlank) {
23900                 this.clearInvalid();
23901                 return true;
23902             }
23903
23904             this.markInvalid(this.errors.PwdEmpty);
23905             this.errorMsg = this.errors.PwdEmpty;
23906             return false;
23907         }
23908         
23909         if(this.insecure){
23910             return true;
23911         }
23912         
23913         if (!value.match(/[\x21-\x7e]+/)) {
23914             this.markInvalid(this.errors.PwdBadChar);
23915             this.errorMsg = this.errors.PwdBadChar;
23916             return false;
23917         }
23918         if (value.length < 6) {
23919             this.markInvalid(this.errors.PwdShort);
23920             this.errorMsg = this.errors.PwdShort;
23921             return false;
23922         }
23923         if (value.length > 16) {
23924             this.markInvalid(this.errors.PwdLong);
23925             this.errorMsg = this.errors.PwdLong;
23926             return false;
23927         }
23928         var strength;
23929         if (this.ClientSideStrongPassword(value)) {
23930             strength = 3;
23931         } else if (this.ClientSideMediumPassword(value)) {
23932             strength = 2;
23933         } else if (this.ClientSideWeakPassword(value)) {
23934             strength = 1;
23935         } else {
23936             strength = 0;
23937         }
23938
23939         
23940         if (strength < 2) {
23941             //this.markInvalid(this.errors.TooWeak);
23942             this.errorMsg = this.errors.TooWeak;
23943             //return false;
23944         }
23945         
23946         
23947         console.log('strength2: ' + strength);
23948         
23949         //var pm = this.trigger.child('div/div/div').dom;
23950         
23951         var pm = this.trigger.child('div/div');
23952         pm.removeClass(this.meterClass);
23953         pm.addClass(this.meterClass[strength]);
23954                 
23955         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23956                 
23957         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23958         
23959         this.errorMsg = ''; 
23960         return true;
23961     },
23962     // private
23963     CharacterSetChecks: function (type)
23964     {
23965         this.type = type;
23966         this.fResult = false;
23967     },
23968     // private
23969     isctype: function (character, type)
23970     {
23971         switch (type) {  
23972             case this.kCapitalLetter:
23973                 if (character >= 'A' && character <= 'Z') {
23974                     return true;
23975                 }
23976                 break;
23977             
23978             case this.kSmallLetter:
23979                 if (character >= 'a' && character <= 'z') {
23980                     return true;
23981                 }
23982                 break;
23983             
23984             case this.kDigit:
23985                 if (character >= '0' && character <= '9') {
23986                     return true;
23987                 }
23988                 break;
23989             
23990             case this.kPunctuation:
23991                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23992                     return true;
23993                 }
23994                 break;
23995             
23996             default:
23997                 return false;
23998         }
23999
24000     },
24001     // private
24002     IsLongEnough: function (pwd, size)
24003     {
24004         return !(pwd == null || isNaN(size) || pwd.length < size);
24005     },
24006     // private
24007     SpansEnoughCharacterSets: function (word, nb)
24008     {
24009         if (!this.IsLongEnough(word, nb))
24010         {
24011             return false;
24012         }
24013
24014         var characterSetChecks = new Array(
24015             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24016             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24017         );
24018         
24019         for (var index = 0; index < word.length; ++index) {
24020             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24021                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24022                     characterSetChecks[nCharSet].fResult = true;
24023                     break;
24024                 }
24025             }
24026         }
24027
24028         var nCharSets = 0;
24029         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24030             if (characterSetChecks[nCharSet].fResult) {
24031                 ++nCharSets;
24032             }
24033         }
24034
24035         if (nCharSets < nb) {
24036             return false;
24037         }
24038         return true;
24039     },
24040     // private
24041     ClientSideStrongPassword: function (pwd)
24042     {
24043         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24044     },
24045     // private
24046     ClientSideMediumPassword: function (pwd)
24047     {
24048         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24049     },
24050     // private
24051     ClientSideWeakPassword: function (pwd)
24052     {
24053         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24054     }
24055           
24056 })//<script type="text/javascript">
24057
24058 /*
24059  * Based  Ext JS Library 1.1.1
24060  * Copyright(c) 2006-2007, Ext JS, LLC.
24061  * LGPL
24062  *
24063  */
24064  
24065 /**
24066  * @class Roo.HtmlEditorCore
24067  * @extends Roo.Component
24068  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24069  *
24070  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24071  */
24072
24073 Roo.HtmlEditorCore = function(config){
24074     
24075     
24076     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24077     
24078     
24079     this.addEvents({
24080         /**
24081          * @event initialize
24082          * Fires when the editor is fully initialized (including the iframe)
24083          * @param {Roo.HtmlEditorCore} this
24084          */
24085         initialize: true,
24086         /**
24087          * @event activate
24088          * Fires when the editor is first receives the focus. Any insertion must wait
24089          * until after this event.
24090          * @param {Roo.HtmlEditorCore} this
24091          */
24092         activate: true,
24093          /**
24094          * @event beforesync
24095          * Fires before the textarea is updated with content from the editor iframe. Return false
24096          * to cancel the sync.
24097          * @param {Roo.HtmlEditorCore} this
24098          * @param {String} html
24099          */
24100         beforesync: true,
24101          /**
24102          * @event beforepush
24103          * Fires before the iframe editor is updated with content from the textarea. Return false
24104          * to cancel the push.
24105          * @param {Roo.HtmlEditorCore} this
24106          * @param {String} html
24107          */
24108         beforepush: true,
24109          /**
24110          * @event sync
24111          * Fires when the textarea is updated with content from the editor iframe.
24112          * @param {Roo.HtmlEditorCore} this
24113          * @param {String} html
24114          */
24115         sync: true,
24116          /**
24117          * @event push
24118          * Fires when the iframe editor is updated with content from the textarea.
24119          * @param {Roo.HtmlEditorCore} this
24120          * @param {String} html
24121          */
24122         push: true,
24123         
24124         /**
24125          * @event editorevent
24126          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24127          * @param {Roo.HtmlEditorCore} this
24128          */
24129         editorevent: true
24130         
24131     });
24132     
24133     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24134     
24135     // defaults : white / black...
24136     this.applyBlacklists();
24137     
24138     
24139     
24140 };
24141
24142
24143 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24144
24145
24146      /**
24147      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24148      */
24149     
24150     owner : false,
24151     
24152      /**
24153      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24154      *                        Roo.resizable.
24155      */
24156     resizable : false,
24157      /**
24158      * @cfg {Number} height (in pixels)
24159      */   
24160     height: 300,
24161    /**
24162      * @cfg {Number} width (in pixels)
24163      */   
24164     width: 500,
24165     
24166     /**
24167      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24168      * 
24169      */
24170     stylesheets: false,
24171     
24172     // id of frame..
24173     frameId: false,
24174     
24175     // private properties
24176     validationEvent : false,
24177     deferHeight: true,
24178     initialized : false,
24179     activated : false,
24180     sourceEditMode : false,
24181     onFocus : Roo.emptyFn,
24182     iframePad:3,
24183     hideMode:'offsets',
24184     
24185     clearUp: true,
24186     
24187     // blacklist + whitelisted elements..
24188     black: false,
24189     white: false,
24190      
24191     bodyCls : '',
24192
24193     /**
24194      * Protected method that will not generally be called directly. It
24195      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24196      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24197      */
24198     getDocMarkup : function(){
24199         // body styles..
24200         var st = '';
24201         
24202         // inherit styels from page...?? 
24203         if (this.stylesheets === false) {
24204             
24205             Roo.get(document.head).select('style').each(function(node) {
24206                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24207             });
24208             
24209             Roo.get(document.head).select('link').each(function(node) { 
24210                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24211             });
24212             
24213         } else if (!this.stylesheets.length) {
24214                 // simple..
24215                 st = '<style type="text/css">' +
24216                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24217                    '</style>';
24218         } else {
24219             for (var i in this.stylesheets) { 
24220                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24221             }
24222             
24223         }
24224         
24225         st +=  '<style type="text/css">' +
24226             'IMG { cursor: pointer } ' +
24227         '</style>';
24228
24229         var cls = 'roo-htmleditor-body';
24230         
24231         if(this.bodyCls.length){
24232             cls += ' ' + this.bodyCls;
24233         }
24234         
24235         return '<html><head>' + st  +
24236             //<style type="text/css">' +
24237             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24238             //'</style>' +
24239             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24240     },
24241
24242     // private
24243     onRender : function(ct, position)
24244     {
24245         var _t = this;
24246         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24247         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24248         
24249         
24250         this.el.dom.style.border = '0 none';
24251         this.el.dom.setAttribute('tabIndex', -1);
24252         this.el.addClass('x-hidden hide');
24253         
24254         
24255         
24256         if(Roo.isIE){ // fix IE 1px bogus margin
24257             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24258         }
24259        
24260         
24261         this.frameId = Roo.id();
24262         
24263          
24264         
24265         var iframe = this.owner.wrap.createChild({
24266             tag: 'iframe',
24267             cls: 'form-control', // bootstrap..
24268             id: this.frameId,
24269             name: this.frameId,
24270             frameBorder : 'no',
24271             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24272         }, this.el
24273         );
24274         
24275         
24276         this.iframe = iframe.dom;
24277
24278          this.assignDocWin();
24279         
24280         this.doc.designMode = 'on';
24281        
24282         this.doc.open();
24283         this.doc.write(this.getDocMarkup());
24284         this.doc.close();
24285
24286         
24287         var task = { // must defer to wait for browser to be ready
24288             run : function(){
24289                 //console.log("run task?" + this.doc.readyState);
24290                 this.assignDocWin();
24291                 if(this.doc.body || this.doc.readyState == 'complete'){
24292                     try {
24293                         this.doc.designMode="on";
24294                     } catch (e) {
24295                         return;
24296                     }
24297                     Roo.TaskMgr.stop(task);
24298                     this.initEditor.defer(10, this);
24299                 }
24300             },
24301             interval : 10,
24302             duration: 10000,
24303             scope: this
24304         };
24305         Roo.TaskMgr.start(task);
24306
24307     },
24308
24309     // private
24310     onResize : function(w, h)
24311     {
24312          Roo.log('resize: ' +w + ',' + h );
24313         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24314         if(!this.iframe){
24315             return;
24316         }
24317         if(typeof w == 'number'){
24318             
24319             this.iframe.style.width = w + 'px';
24320         }
24321         if(typeof h == 'number'){
24322             
24323             this.iframe.style.height = h + 'px';
24324             if(this.doc){
24325                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24326             }
24327         }
24328         
24329     },
24330
24331     /**
24332      * Toggles the editor between standard and source edit mode.
24333      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24334      */
24335     toggleSourceEdit : function(sourceEditMode){
24336         
24337         this.sourceEditMode = sourceEditMode === true;
24338         
24339         if(this.sourceEditMode){
24340  
24341             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24342             
24343         }else{
24344             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24345             //this.iframe.className = '';
24346             this.deferFocus();
24347         }
24348         //this.setSize(this.owner.wrap.getSize());
24349         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24350     },
24351
24352     
24353   
24354
24355     /**
24356      * Protected method that will not generally be called directly. If you need/want
24357      * custom HTML cleanup, this is the method you should override.
24358      * @param {String} html The HTML to be cleaned
24359      * return {String} The cleaned HTML
24360      */
24361     cleanHtml : function(html){
24362         html = String(html);
24363         if(html.length > 5){
24364             if(Roo.isSafari){ // strip safari nonsense
24365                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24366             }
24367         }
24368         if(html == '&nbsp;'){
24369             html = '';
24370         }
24371         return html;
24372     },
24373
24374     /**
24375      * HTML Editor -> Textarea
24376      * Protected method that will not generally be called directly. Syncs the contents
24377      * of the editor iframe with the textarea.
24378      */
24379     syncValue : function(){
24380         if(this.initialized){
24381             var bd = (this.doc.body || this.doc.documentElement);
24382             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24383             var html = bd.innerHTML;
24384             if(Roo.isSafari){
24385                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24386                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24387                 if(m && m[1]){
24388                     html = '<div style="'+m[0]+'">' + html + '</div>';
24389                 }
24390             }
24391             html = this.cleanHtml(html);
24392             // fix up the special chars.. normaly like back quotes in word...
24393             // however we do not want to do this with chinese..
24394             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24395                 
24396                 var cc = match.charCodeAt();
24397
24398                 // Get the character value, handling surrogate pairs
24399                 if (match.length == 2) {
24400                     // It's a surrogate pair, calculate the Unicode code point
24401                     var high = match.charCodeAt(0) - 0xD800;
24402                     var low  = match.charCodeAt(1) - 0xDC00;
24403                     cc = (high * 0x400) + low + 0x10000;
24404                 }  else if (
24405                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24406                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24407                     (cc >= 0xf900 && cc < 0xfb00 )
24408                 ) {
24409                         return match;
24410                 }  
24411          
24412                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24413                 return "&#" + cc + ";";
24414                 
24415                 
24416             });
24417             
24418             
24419              
24420             if(this.owner.fireEvent('beforesync', this, html) !== false){
24421                 this.el.dom.value = html;
24422                 this.owner.fireEvent('sync', this, html);
24423             }
24424         }
24425     },
24426
24427     /**
24428      * Protected method that will not generally be called directly. Pushes the value of the textarea
24429      * into the iframe editor.
24430      */
24431     pushValue : function(){
24432         if(this.initialized){
24433             var v = this.el.dom.value.trim();
24434             
24435 //            if(v.length < 1){
24436 //                v = '&#160;';
24437 //            }
24438             
24439             if(this.owner.fireEvent('beforepush', this, v) !== false){
24440                 var d = (this.doc.body || this.doc.documentElement);
24441                 d.innerHTML = v;
24442                 this.cleanUpPaste();
24443                 this.el.dom.value = d.innerHTML;
24444                 this.owner.fireEvent('push', this, v);
24445             }
24446         }
24447     },
24448
24449     // private
24450     deferFocus : function(){
24451         this.focus.defer(10, this);
24452     },
24453
24454     // doc'ed in Field
24455     focus : function(){
24456         if(this.win && !this.sourceEditMode){
24457             this.win.focus();
24458         }else{
24459             this.el.focus();
24460         }
24461     },
24462     
24463     assignDocWin: function()
24464     {
24465         var iframe = this.iframe;
24466         
24467          if(Roo.isIE){
24468             this.doc = iframe.contentWindow.document;
24469             this.win = iframe.contentWindow;
24470         } else {
24471 //            if (!Roo.get(this.frameId)) {
24472 //                return;
24473 //            }
24474 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24475 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24476             
24477             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24478                 return;
24479             }
24480             
24481             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24482             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24483         }
24484     },
24485     
24486     // private
24487     initEditor : function(){
24488         //console.log("INIT EDITOR");
24489         this.assignDocWin();
24490         
24491         
24492         
24493         this.doc.designMode="on";
24494         this.doc.open();
24495         this.doc.write(this.getDocMarkup());
24496         this.doc.close();
24497         
24498         var dbody = (this.doc.body || this.doc.documentElement);
24499         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24500         // this copies styles from the containing element into thsi one..
24501         // not sure why we need all of this..
24502         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24503         
24504         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24505         //ss['background-attachment'] = 'fixed'; // w3c
24506         dbody.bgProperties = 'fixed'; // ie
24507         //Roo.DomHelper.applyStyles(dbody, ss);
24508         Roo.EventManager.on(this.doc, {
24509             //'mousedown': this.onEditorEvent,
24510             'mouseup': this.onEditorEvent,
24511             'dblclick': this.onEditorEvent,
24512             'click': this.onEditorEvent,
24513             'keyup': this.onEditorEvent,
24514             buffer:100,
24515             scope: this
24516         });
24517         if(Roo.isGecko){
24518             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24519         }
24520         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24521             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24522         }
24523         this.initialized = true;
24524
24525         this.owner.fireEvent('initialize', this);
24526         this.pushValue();
24527     },
24528
24529     // private
24530     onDestroy : function(){
24531         
24532         
24533         
24534         if(this.rendered){
24535             
24536             //for (var i =0; i < this.toolbars.length;i++) {
24537             //    // fixme - ask toolbars for heights?
24538             //    this.toolbars[i].onDestroy();
24539            // }
24540             
24541             //this.wrap.dom.innerHTML = '';
24542             //this.wrap.remove();
24543         }
24544     },
24545
24546     // private
24547     onFirstFocus : function(){
24548         
24549         this.assignDocWin();
24550         
24551         
24552         this.activated = true;
24553          
24554     
24555         if(Roo.isGecko){ // prevent silly gecko errors
24556             this.win.focus();
24557             var s = this.win.getSelection();
24558             if(!s.focusNode || s.focusNode.nodeType != 3){
24559                 var r = s.getRangeAt(0);
24560                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24561                 r.collapse(true);
24562                 this.deferFocus();
24563             }
24564             try{
24565                 this.execCmd('useCSS', true);
24566                 this.execCmd('styleWithCSS', false);
24567             }catch(e){}
24568         }
24569         this.owner.fireEvent('activate', this);
24570     },
24571
24572     // private
24573     adjustFont: function(btn){
24574         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24575         //if(Roo.isSafari){ // safari
24576         //    adjust *= 2;
24577        // }
24578         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24579         if(Roo.isSafari){ // safari
24580             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24581             v =  (v < 10) ? 10 : v;
24582             v =  (v > 48) ? 48 : v;
24583             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24584             
24585         }
24586         
24587         
24588         v = Math.max(1, v+adjust);
24589         
24590         this.execCmd('FontSize', v  );
24591     },
24592
24593     onEditorEvent : function(e)
24594     {
24595         this.owner.fireEvent('editorevent', this, e);
24596       //  this.updateToolbar();
24597         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24598     },
24599
24600     insertTag : function(tg)
24601     {
24602         // could be a bit smarter... -> wrap the current selected tRoo..
24603         if (tg.toLowerCase() == 'span' ||
24604             tg.toLowerCase() == 'code' ||
24605             tg.toLowerCase() == 'sup' ||
24606             tg.toLowerCase() == 'sub' 
24607             ) {
24608             
24609             range = this.createRange(this.getSelection());
24610             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24611             wrappingNode.appendChild(range.extractContents());
24612             range.insertNode(wrappingNode);
24613
24614             return;
24615             
24616             
24617             
24618         }
24619         this.execCmd("formatblock",   tg);
24620         
24621     },
24622     
24623     insertText : function(txt)
24624     {
24625         
24626         
24627         var range = this.createRange();
24628         range.deleteContents();
24629                //alert(Sender.getAttribute('label'));
24630                
24631         range.insertNode(this.doc.createTextNode(txt));
24632     } ,
24633     
24634      
24635
24636     /**
24637      * Executes a Midas editor command on the editor document and performs necessary focus and
24638      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24639      * @param {String} cmd The Midas command
24640      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24641      */
24642     relayCmd : function(cmd, value){
24643         this.win.focus();
24644         this.execCmd(cmd, value);
24645         this.owner.fireEvent('editorevent', this);
24646         //this.updateToolbar();
24647         this.owner.deferFocus();
24648     },
24649
24650     /**
24651      * Executes a Midas editor command directly on the editor document.
24652      * For visual commands, you should use {@link #relayCmd} instead.
24653      * <b>This should only be called after the editor is initialized.</b>
24654      * @param {String} cmd The Midas command
24655      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24656      */
24657     execCmd : function(cmd, value){
24658         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24659         this.syncValue();
24660     },
24661  
24662  
24663    
24664     /**
24665      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24666      * to insert tRoo.
24667      * @param {String} text | dom node.. 
24668      */
24669     insertAtCursor : function(text)
24670     {
24671         
24672         if(!this.activated){
24673             return;
24674         }
24675         /*
24676         if(Roo.isIE){
24677             this.win.focus();
24678             var r = this.doc.selection.createRange();
24679             if(r){
24680                 r.collapse(true);
24681                 r.pasteHTML(text);
24682                 this.syncValue();
24683                 this.deferFocus();
24684             
24685             }
24686             return;
24687         }
24688         */
24689         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24690             this.win.focus();
24691             
24692             
24693             // from jquery ui (MIT licenced)
24694             var range, node;
24695             var win = this.win;
24696             
24697             if (win.getSelection && win.getSelection().getRangeAt) {
24698                 range = win.getSelection().getRangeAt(0);
24699                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24700                 range.insertNode(node);
24701             } else if (win.document.selection && win.document.selection.createRange) {
24702                 // no firefox support
24703                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24704                 win.document.selection.createRange().pasteHTML(txt);
24705             } else {
24706                 // no firefox support
24707                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24708                 this.execCmd('InsertHTML', txt);
24709             } 
24710             
24711             this.syncValue();
24712             
24713             this.deferFocus();
24714         }
24715     },
24716  // private
24717     mozKeyPress : function(e){
24718         if(e.ctrlKey){
24719             var c = e.getCharCode(), cmd;
24720           
24721             if(c > 0){
24722                 c = String.fromCharCode(c).toLowerCase();
24723                 switch(c){
24724                     case 'b':
24725                         cmd = 'bold';
24726                         break;
24727                     case 'i':
24728                         cmd = 'italic';
24729                         break;
24730                     
24731                     case 'u':
24732                         cmd = 'underline';
24733                         break;
24734                     
24735                     case 'v':
24736                         this.cleanUpPaste.defer(100, this);
24737                         return;
24738                         
24739                 }
24740                 if(cmd){
24741                     this.win.focus();
24742                     this.execCmd(cmd);
24743                     this.deferFocus();
24744                     e.preventDefault();
24745                 }
24746                 
24747             }
24748         }
24749     },
24750
24751     // private
24752     fixKeys : function(){ // load time branching for fastest keydown performance
24753         if(Roo.isIE){
24754             return function(e){
24755                 var k = e.getKey(), r;
24756                 if(k == e.TAB){
24757                     e.stopEvent();
24758                     r = this.doc.selection.createRange();
24759                     if(r){
24760                         r.collapse(true);
24761                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24762                         this.deferFocus();
24763                     }
24764                     return;
24765                 }
24766                 
24767                 if(k == e.ENTER){
24768                     r = this.doc.selection.createRange();
24769                     if(r){
24770                         var target = r.parentElement();
24771                         if(!target || target.tagName.toLowerCase() != 'li'){
24772                             e.stopEvent();
24773                             r.pasteHTML('<br />');
24774                             r.collapse(false);
24775                             r.select();
24776                         }
24777                     }
24778                 }
24779                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24780                     this.cleanUpPaste.defer(100, this);
24781                     return;
24782                 }
24783                 
24784                 
24785             };
24786         }else if(Roo.isOpera){
24787             return function(e){
24788                 var k = e.getKey();
24789                 if(k == e.TAB){
24790                     e.stopEvent();
24791                     this.win.focus();
24792                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24793                     this.deferFocus();
24794                 }
24795                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24796                     this.cleanUpPaste.defer(100, this);
24797                     return;
24798                 }
24799                 
24800             };
24801         }else if(Roo.isSafari){
24802             return function(e){
24803                 var k = e.getKey();
24804                 
24805                 if(k == e.TAB){
24806                     e.stopEvent();
24807                     this.execCmd('InsertText','\t');
24808                     this.deferFocus();
24809                     return;
24810                 }
24811                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24812                     this.cleanUpPaste.defer(100, this);
24813                     return;
24814                 }
24815                 
24816              };
24817         }
24818     }(),
24819     
24820     getAllAncestors: function()
24821     {
24822         var p = this.getSelectedNode();
24823         var a = [];
24824         if (!p) {
24825             a.push(p); // push blank onto stack..
24826             p = this.getParentElement();
24827         }
24828         
24829         
24830         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24831             a.push(p);
24832             p = p.parentNode;
24833         }
24834         a.push(this.doc.body);
24835         return a;
24836     },
24837     lastSel : false,
24838     lastSelNode : false,
24839     
24840     
24841     getSelection : function() 
24842     {
24843         this.assignDocWin();
24844         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24845     },
24846     
24847     getSelectedNode: function() 
24848     {
24849         // this may only work on Gecko!!!
24850         
24851         // should we cache this!!!!
24852         
24853         
24854         
24855          
24856         var range = this.createRange(this.getSelection()).cloneRange();
24857         
24858         if (Roo.isIE) {
24859             var parent = range.parentElement();
24860             while (true) {
24861                 var testRange = range.duplicate();
24862                 testRange.moveToElementText(parent);
24863                 if (testRange.inRange(range)) {
24864                     break;
24865                 }
24866                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24867                     break;
24868                 }
24869                 parent = parent.parentElement;
24870             }
24871             return parent;
24872         }
24873         
24874         // is ancestor a text element.
24875         var ac =  range.commonAncestorContainer;
24876         if (ac.nodeType == 3) {
24877             ac = ac.parentNode;
24878         }
24879         
24880         var ar = ac.childNodes;
24881          
24882         var nodes = [];
24883         var other_nodes = [];
24884         var has_other_nodes = false;
24885         for (var i=0;i<ar.length;i++) {
24886             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24887                 continue;
24888             }
24889             // fullly contained node.
24890             
24891             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24892                 nodes.push(ar[i]);
24893                 continue;
24894             }
24895             
24896             // probably selected..
24897             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24898                 other_nodes.push(ar[i]);
24899                 continue;
24900             }
24901             // outer..
24902             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24903                 continue;
24904             }
24905             
24906             
24907             has_other_nodes = true;
24908         }
24909         if (!nodes.length && other_nodes.length) {
24910             nodes= other_nodes;
24911         }
24912         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24913             return false;
24914         }
24915         
24916         return nodes[0];
24917     },
24918     createRange: function(sel)
24919     {
24920         // this has strange effects when using with 
24921         // top toolbar - not sure if it's a great idea.
24922         //this.editor.contentWindow.focus();
24923         if (typeof sel != "undefined") {
24924             try {
24925                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24926             } catch(e) {
24927                 return this.doc.createRange();
24928             }
24929         } else {
24930             return this.doc.createRange();
24931         }
24932     },
24933     getParentElement: function()
24934     {
24935         
24936         this.assignDocWin();
24937         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24938         
24939         var range = this.createRange(sel);
24940          
24941         try {
24942             var p = range.commonAncestorContainer;
24943             while (p.nodeType == 3) { // text node
24944                 p = p.parentNode;
24945             }
24946             return p;
24947         } catch (e) {
24948             return null;
24949         }
24950     
24951     },
24952     /***
24953      *
24954      * Range intersection.. the hard stuff...
24955      *  '-1' = before
24956      *  '0' = hits..
24957      *  '1' = after.
24958      *         [ -- selected range --- ]
24959      *   [fail]                        [fail]
24960      *
24961      *    basically..
24962      *      if end is before start or  hits it. fail.
24963      *      if start is after end or hits it fail.
24964      *
24965      *   if either hits (but other is outside. - then it's not 
24966      *   
24967      *    
24968      **/
24969     
24970     
24971     // @see http://www.thismuchiknow.co.uk/?p=64.
24972     rangeIntersectsNode : function(range, node)
24973     {
24974         var nodeRange = node.ownerDocument.createRange();
24975         try {
24976             nodeRange.selectNode(node);
24977         } catch (e) {
24978             nodeRange.selectNodeContents(node);
24979         }
24980     
24981         var rangeStartRange = range.cloneRange();
24982         rangeStartRange.collapse(true);
24983     
24984         var rangeEndRange = range.cloneRange();
24985         rangeEndRange.collapse(false);
24986     
24987         var nodeStartRange = nodeRange.cloneRange();
24988         nodeStartRange.collapse(true);
24989     
24990         var nodeEndRange = nodeRange.cloneRange();
24991         nodeEndRange.collapse(false);
24992     
24993         return rangeStartRange.compareBoundaryPoints(
24994                  Range.START_TO_START, nodeEndRange) == -1 &&
24995                rangeEndRange.compareBoundaryPoints(
24996                  Range.START_TO_START, nodeStartRange) == 1;
24997         
24998          
24999     },
25000     rangeCompareNode : function(range, node)
25001     {
25002         var nodeRange = node.ownerDocument.createRange();
25003         try {
25004             nodeRange.selectNode(node);
25005         } catch (e) {
25006             nodeRange.selectNodeContents(node);
25007         }
25008         
25009         
25010         range.collapse(true);
25011     
25012         nodeRange.collapse(true);
25013      
25014         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25015         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25016          
25017         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25018         
25019         var nodeIsBefore   =  ss == 1;
25020         var nodeIsAfter    = ee == -1;
25021         
25022         if (nodeIsBefore && nodeIsAfter) {
25023             return 0; // outer
25024         }
25025         if (!nodeIsBefore && nodeIsAfter) {
25026             return 1; //right trailed.
25027         }
25028         
25029         if (nodeIsBefore && !nodeIsAfter) {
25030             return 2;  // left trailed.
25031         }
25032         // fully contined.
25033         return 3;
25034     },
25035
25036     // private? - in a new class?
25037     cleanUpPaste :  function()
25038     {
25039         // cleans up the whole document..
25040         Roo.log('cleanuppaste');
25041         
25042         this.cleanUpChildren(this.doc.body);
25043         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25044         if (clean != this.doc.body.innerHTML) {
25045             this.doc.body.innerHTML = clean;
25046         }
25047         
25048     },
25049     
25050     cleanWordChars : function(input) {// change the chars to hex code
25051         var he = Roo.HtmlEditorCore;
25052         
25053         var output = input;
25054         Roo.each(he.swapCodes, function(sw) { 
25055             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25056             
25057             output = output.replace(swapper, sw[1]);
25058         });
25059         
25060         return output;
25061     },
25062     
25063     
25064     cleanUpChildren : function (n)
25065     {
25066         if (!n.childNodes.length) {
25067             return;
25068         }
25069         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25070            this.cleanUpChild(n.childNodes[i]);
25071         }
25072     },
25073     
25074     
25075         
25076     
25077     cleanUpChild : function (node)
25078     {
25079         var ed = this;
25080         //console.log(node);
25081         if (node.nodeName == "#text") {
25082             // clean up silly Windows -- stuff?
25083             return; 
25084         }
25085         if (node.nodeName == "#comment") {
25086             node.parentNode.removeChild(node);
25087             // clean up silly Windows -- stuff?
25088             return; 
25089         }
25090         var lcname = node.tagName.toLowerCase();
25091         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25092         // whitelist of tags..
25093         
25094         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25095             // remove node.
25096             node.parentNode.removeChild(node);
25097             return;
25098             
25099         }
25100         
25101         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25102         
25103         // spans with no attributes - just remove them..
25104         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
25105             remove_keep_children = true;
25106         }
25107         
25108         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25109         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25110         
25111         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25112         //    remove_keep_children = true;
25113         //}
25114         
25115         if (remove_keep_children) {
25116             this.cleanUpChildren(node);
25117             // inserts everything just before this node...
25118             while (node.childNodes.length) {
25119                 var cn = node.childNodes[0];
25120                 node.removeChild(cn);
25121                 node.parentNode.insertBefore(cn, node);
25122             }
25123             node.parentNode.removeChild(node);
25124             return;
25125         }
25126         
25127         if (!node.attributes || !node.attributes.length) {
25128             
25129           
25130             
25131             
25132             this.cleanUpChildren(node);
25133             return;
25134         }
25135         
25136         function cleanAttr(n,v)
25137         {
25138             
25139             if (v.match(/^\./) || v.match(/^\//)) {
25140                 return;
25141             }
25142             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25143                 return;
25144             }
25145             if (v.match(/^#/)) {
25146                 return;
25147             }
25148             if (v.match(/^\{/)) { // allow template editing.
25149                 return;
25150             }
25151 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25152             node.removeAttribute(n);
25153             
25154         }
25155         
25156         var cwhite = this.cwhite;
25157         var cblack = this.cblack;
25158             
25159         function cleanStyle(n,v)
25160         {
25161             if (v.match(/expression/)) { //XSS?? should we even bother..
25162                 node.removeAttribute(n);
25163                 return;
25164             }
25165             
25166             var parts = v.split(/;/);
25167             var clean = [];
25168             
25169             Roo.each(parts, function(p) {
25170                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25171                 if (!p.length) {
25172                     return true;
25173                 }
25174                 var l = p.split(':').shift().replace(/\s+/g,'');
25175                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25176                 
25177                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25178 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25179                     //node.removeAttribute(n);
25180                     return true;
25181                 }
25182                 //Roo.log()
25183                 // only allow 'c whitelisted system attributes'
25184                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25185 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25186                     //node.removeAttribute(n);
25187                     return true;
25188                 }
25189                 
25190                 
25191                  
25192                 
25193                 clean.push(p);
25194                 return true;
25195             });
25196             if (clean.length) { 
25197                 node.setAttribute(n, clean.join(';'));
25198             } else {
25199                 node.removeAttribute(n);
25200             }
25201             
25202         }
25203         
25204         
25205         for (var i = node.attributes.length-1; i > -1 ; i--) {
25206             var a = node.attributes[i];
25207             //console.log(a);
25208             
25209             if (a.name.toLowerCase().substr(0,2)=='on')  {
25210                 node.removeAttribute(a.name);
25211                 continue;
25212             }
25213             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25214                 node.removeAttribute(a.name);
25215                 continue;
25216             }
25217             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25218                 cleanAttr(a.name,a.value); // fixme..
25219                 continue;
25220             }
25221             if (a.name == 'style') {
25222                 cleanStyle(a.name,a.value);
25223                 continue;
25224             }
25225             /// clean up MS crap..
25226             // tecnically this should be a list of valid class'es..
25227             
25228             
25229             if (a.name == 'class') {
25230                 if (a.value.match(/^Mso/)) {
25231                     node.removeAttribute('class');
25232                 }
25233                 
25234                 if (a.value.match(/^body$/)) {
25235                     node.removeAttribute('class');
25236                 }
25237                 continue;
25238             }
25239             
25240             // style cleanup!?
25241             // class cleanup?
25242             
25243         }
25244         
25245         
25246         this.cleanUpChildren(node);
25247         
25248         
25249     },
25250     
25251     /**
25252      * Clean up MS wordisms...
25253      */
25254     cleanWord : function(node)
25255     {
25256         if (!node) {
25257             this.cleanWord(this.doc.body);
25258             return;
25259         }
25260         
25261         if(
25262                 node.nodeName == 'SPAN' &&
25263                 !node.hasAttributes() &&
25264                 node.childNodes.length == 1 &&
25265                 node.firstChild.nodeName == "#text"  
25266         ) {
25267             var textNode = node.firstChild;
25268             node.removeChild(textNode);
25269             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25270                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25271             }
25272             node.parentNode.insertBefore(textNode, node);
25273             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25274                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25275             }
25276             node.parentNode.removeChild(node);
25277         }
25278         
25279         if (node.nodeName == "#text") {
25280             // clean up silly Windows -- stuff?
25281             return; 
25282         }
25283         if (node.nodeName == "#comment") {
25284             node.parentNode.removeChild(node);
25285             // clean up silly Windows -- stuff?
25286             return; 
25287         }
25288         
25289         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25290             node.parentNode.removeChild(node);
25291             return;
25292         }
25293         //Roo.log(node.tagName);
25294         // remove - but keep children..
25295         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25296             //Roo.log('-- removed');
25297             while (node.childNodes.length) {
25298                 var cn = node.childNodes[0];
25299                 node.removeChild(cn);
25300                 node.parentNode.insertBefore(cn, node);
25301                 // move node to parent - and clean it..
25302                 this.cleanWord(cn);
25303             }
25304             node.parentNode.removeChild(node);
25305             /// no need to iterate chidlren = it's got none..
25306             //this.iterateChildren(node, this.cleanWord);
25307             return;
25308         }
25309         // clean styles
25310         if (node.className.length) {
25311             
25312             var cn = node.className.split(/\W+/);
25313             var cna = [];
25314             Roo.each(cn, function(cls) {
25315                 if (cls.match(/Mso[a-zA-Z]+/)) {
25316                     return;
25317                 }
25318                 cna.push(cls);
25319             });
25320             node.className = cna.length ? cna.join(' ') : '';
25321             if (!cna.length) {
25322                 node.removeAttribute("class");
25323             }
25324         }
25325         
25326         if (node.hasAttribute("lang")) {
25327             node.removeAttribute("lang");
25328         }
25329         
25330         if (node.hasAttribute("style")) {
25331             
25332             var styles = node.getAttribute("style").split(";");
25333             var nstyle = [];
25334             Roo.each(styles, function(s) {
25335                 if (!s.match(/:/)) {
25336                     return;
25337                 }
25338                 var kv = s.split(":");
25339                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25340                     return;
25341                 }
25342                 // what ever is left... we allow.
25343                 nstyle.push(s);
25344             });
25345             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25346             if (!nstyle.length) {
25347                 node.removeAttribute('style');
25348             }
25349         }
25350         this.iterateChildren(node, this.cleanWord);
25351         
25352         
25353         
25354     },
25355     /**
25356      * iterateChildren of a Node, calling fn each time, using this as the scole..
25357      * @param {DomNode} node node to iterate children of.
25358      * @param {Function} fn method of this class to call on each item.
25359      */
25360     iterateChildren : function(node, fn)
25361     {
25362         if (!node.childNodes.length) {
25363                 return;
25364         }
25365         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25366            fn.call(this, node.childNodes[i])
25367         }
25368     },
25369     
25370     
25371     /**
25372      * cleanTableWidths.
25373      *
25374      * Quite often pasting from word etc.. results in tables with column and widths.
25375      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25376      *
25377      */
25378     cleanTableWidths : function(node)
25379     {
25380          
25381          
25382         if (!node) {
25383             this.cleanTableWidths(this.doc.body);
25384             return;
25385         }
25386         
25387         // ignore list...
25388         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25389             return; 
25390         }
25391         Roo.log(node.tagName);
25392         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25393             this.iterateChildren(node, this.cleanTableWidths);
25394             return;
25395         }
25396         if (node.hasAttribute('width')) {
25397             node.removeAttribute('width');
25398         }
25399         
25400          
25401         if (node.hasAttribute("style")) {
25402             // pretty basic...
25403             
25404             var styles = node.getAttribute("style").split(";");
25405             var nstyle = [];
25406             Roo.each(styles, function(s) {
25407                 if (!s.match(/:/)) {
25408                     return;
25409                 }
25410                 var kv = s.split(":");
25411                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25412                     return;
25413                 }
25414                 // what ever is left... we allow.
25415                 nstyle.push(s);
25416             });
25417             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25418             if (!nstyle.length) {
25419                 node.removeAttribute('style');
25420             }
25421         }
25422         
25423         this.iterateChildren(node, this.cleanTableWidths);
25424         
25425         
25426     },
25427     
25428     
25429     
25430     
25431     domToHTML : function(currentElement, depth, nopadtext) {
25432         
25433         depth = depth || 0;
25434         nopadtext = nopadtext || false;
25435     
25436         if (!currentElement) {
25437             return this.domToHTML(this.doc.body);
25438         }
25439         
25440         //Roo.log(currentElement);
25441         var j;
25442         var allText = false;
25443         var nodeName = currentElement.nodeName;
25444         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25445         
25446         if  (nodeName == '#text') {
25447             
25448             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25449         }
25450         
25451         
25452         var ret = '';
25453         if (nodeName != 'BODY') {
25454              
25455             var i = 0;
25456             // Prints the node tagName, such as <A>, <IMG>, etc
25457             if (tagName) {
25458                 var attr = [];
25459                 for(i = 0; i < currentElement.attributes.length;i++) {
25460                     // quoting?
25461                     var aname = currentElement.attributes.item(i).name;
25462                     if (!currentElement.attributes.item(i).value.length) {
25463                         continue;
25464                     }
25465                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25466                 }
25467                 
25468                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25469             } 
25470             else {
25471                 
25472                 // eack
25473             }
25474         } else {
25475             tagName = false;
25476         }
25477         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25478             return ret;
25479         }
25480         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25481             nopadtext = true;
25482         }
25483         
25484         
25485         // Traverse the tree
25486         i = 0;
25487         var currentElementChild = currentElement.childNodes.item(i);
25488         var allText = true;
25489         var innerHTML  = '';
25490         lastnode = '';
25491         while (currentElementChild) {
25492             // Formatting code (indent the tree so it looks nice on the screen)
25493             var nopad = nopadtext;
25494             if (lastnode == 'SPAN') {
25495                 nopad  = true;
25496             }
25497             // text
25498             if  (currentElementChild.nodeName == '#text') {
25499                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25500                 toadd = nopadtext ? toadd : toadd.trim();
25501                 if (!nopad && toadd.length > 80) {
25502                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25503                 }
25504                 innerHTML  += toadd;
25505                 
25506                 i++;
25507                 currentElementChild = currentElement.childNodes.item(i);
25508                 lastNode = '';
25509                 continue;
25510             }
25511             allText = false;
25512             
25513             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25514                 
25515             // Recursively traverse the tree structure of the child node
25516             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25517             lastnode = currentElementChild.nodeName;
25518             i++;
25519             currentElementChild=currentElement.childNodes.item(i);
25520         }
25521         
25522         ret += innerHTML;
25523         
25524         if (!allText) {
25525                 // The remaining code is mostly for formatting the tree
25526             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25527         }
25528         
25529         
25530         if (tagName) {
25531             ret+= "</"+tagName+">";
25532         }
25533         return ret;
25534         
25535     },
25536         
25537     applyBlacklists : function()
25538     {
25539         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25540         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25541         
25542         this.white = [];
25543         this.black = [];
25544         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25545             if (b.indexOf(tag) > -1) {
25546                 return;
25547             }
25548             this.white.push(tag);
25549             
25550         }, this);
25551         
25552         Roo.each(w, function(tag) {
25553             if (b.indexOf(tag) > -1) {
25554                 return;
25555             }
25556             if (this.white.indexOf(tag) > -1) {
25557                 return;
25558             }
25559             this.white.push(tag);
25560             
25561         }, this);
25562         
25563         
25564         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25565             if (w.indexOf(tag) > -1) {
25566                 return;
25567             }
25568             this.black.push(tag);
25569             
25570         }, this);
25571         
25572         Roo.each(b, function(tag) {
25573             if (w.indexOf(tag) > -1) {
25574                 return;
25575             }
25576             if (this.black.indexOf(tag) > -1) {
25577                 return;
25578             }
25579             this.black.push(tag);
25580             
25581         }, this);
25582         
25583         
25584         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25585         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25586         
25587         this.cwhite = [];
25588         this.cblack = [];
25589         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25590             if (b.indexOf(tag) > -1) {
25591                 return;
25592             }
25593             this.cwhite.push(tag);
25594             
25595         }, this);
25596         
25597         Roo.each(w, function(tag) {
25598             if (b.indexOf(tag) > -1) {
25599                 return;
25600             }
25601             if (this.cwhite.indexOf(tag) > -1) {
25602                 return;
25603             }
25604             this.cwhite.push(tag);
25605             
25606         }, this);
25607         
25608         
25609         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25610             if (w.indexOf(tag) > -1) {
25611                 return;
25612             }
25613             this.cblack.push(tag);
25614             
25615         }, this);
25616         
25617         Roo.each(b, function(tag) {
25618             if (w.indexOf(tag) > -1) {
25619                 return;
25620             }
25621             if (this.cblack.indexOf(tag) > -1) {
25622                 return;
25623             }
25624             this.cblack.push(tag);
25625             
25626         }, this);
25627     },
25628     
25629     setStylesheets : function(stylesheets)
25630     {
25631         if(typeof(stylesheets) == 'string'){
25632             Roo.get(this.iframe.contentDocument.head).createChild({
25633                 tag : 'link',
25634                 rel : 'stylesheet',
25635                 type : 'text/css',
25636                 href : stylesheets
25637             });
25638             
25639             return;
25640         }
25641         var _this = this;
25642      
25643         Roo.each(stylesheets, function(s) {
25644             if(!s.length){
25645                 return;
25646             }
25647             
25648             Roo.get(_this.iframe.contentDocument.head).createChild({
25649                 tag : 'link',
25650                 rel : 'stylesheet',
25651                 type : 'text/css',
25652                 href : s
25653             });
25654         });
25655
25656         
25657     },
25658     
25659     removeStylesheets : function()
25660     {
25661         var _this = this;
25662         
25663         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25664             s.remove();
25665         });
25666     },
25667     
25668     setStyle : function(style)
25669     {
25670         Roo.get(this.iframe.contentDocument.head).createChild({
25671             tag : 'style',
25672             type : 'text/css',
25673             html : style
25674         });
25675
25676         return;
25677     }
25678     
25679     // hide stuff that is not compatible
25680     /**
25681      * @event blur
25682      * @hide
25683      */
25684     /**
25685      * @event change
25686      * @hide
25687      */
25688     /**
25689      * @event focus
25690      * @hide
25691      */
25692     /**
25693      * @event specialkey
25694      * @hide
25695      */
25696     /**
25697      * @cfg {String} fieldClass @hide
25698      */
25699     /**
25700      * @cfg {String} focusClass @hide
25701      */
25702     /**
25703      * @cfg {String} autoCreate @hide
25704      */
25705     /**
25706      * @cfg {String} inputType @hide
25707      */
25708     /**
25709      * @cfg {String} invalidClass @hide
25710      */
25711     /**
25712      * @cfg {String} invalidText @hide
25713      */
25714     /**
25715      * @cfg {String} msgFx @hide
25716      */
25717     /**
25718      * @cfg {String} validateOnBlur @hide
25719      */
25720 });
25721
25722 Roo.HtmlEditorCore.white = [
25723         'area', 'br', 'img', 'input', 'hr', 'wbr',
25724         
25725        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25726        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25727        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25728        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25729        'table',   'ul',         'xmp', 
25730        
25731        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25732       'thead',   'tr', 
25733      
25734       'dir', 'menu', 'ol', 'ul', 'dl',
25735        
25736       'embed',  'object'
25737 ];
25738
25739
25740 Roo.HtmlEditorCore.black = [
25741     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25742         'applet', // 
25743         'base',   'basefont', 'bgsound', 'blink',  'body', 
25744         'frame',  'frameset', 'head',    'html',   'ilayer', 
25745         'iframe', 'layer',  'link',     'meta',    'object',   
25746         'script', 'style' ,'title',  'xml' // clean later..
25747 ];
25748 Roo.HtmlEditorCore.clean = [
25749     'script', 'style', 'title', 'xml'
25750 ];
25751 Roo.HtmlEditorCore.remove = [
25752     'font'
25753 ];
25754 // attributes..
25755
25756 Roo.HtmlEditorCore.ablack = [
25757     'on'
25758 ];
25759     
25760 Roo.HtmlEditorCore.aclean = [ 
25761     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25762 ];
25763
25764 // protocols..
25765 Roo.HtmlEditorCore.pwhite= [
25766         'http',  'https',  'mailto'
25767 ];
25768
25769 // white listed style attributes.
25770 Roo.HtmlEditorCore.cwhite= [
25771       //  'text-align', /// default is to allow most things..
25772       
25773          
25774 //        'font-size'//??
25775 ];
25776
25777 // black listed style attributes.
25778 Roo.HtmlEditorCore.cblack= [
25779       //  'font-size' -- this can be set by the project 
25780 ];
25781
25782
25783 Roo.HtmlEditorCore.swapCodes   =[ 
25784     [    8211, "--" ], 
25785     [    8212, "--" ], 
25786     [    8216,  "'" ],  
25787     [    8217, "'" ],  
25788     [    8220, '"' ],  
25789     [    8221, '"' ],  
25790     [    8226, "*" ],  
25791     [    8230, "..." ]
25792 ]; 
25793
25794     /*
25795  * - LGPL
25796  *
25797  * HtmlEditor
25798  * 
25799  */
25800
25801 /**
25802  * @class Roo.bootstrap.HtmlEditor
25803  * @extends Roo.bootstrap.TextArea
25804  * Bootstrap HtmlEditor class
25805
25806  * @constructor
25807  * Create a new HtmlEditor
25808  * @param {Object} config The config object
25809  */
25810
25811 Roo.bootstrap.HtmlEditor = function(config){
25812     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25813     if (!this.toolbars) {
25814         this.toolbars = [];
25815     }
25816     
25817     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25818     this.addEvents({
25819             /**
25820              * @event initialize
25821              * Fires when the editor is fully initialized (including the iframe)
25822              * @param {HtmlEditor} this
25823              */
25824             initialize: true,
25825             /**
25826              * @event activate
25827              * Fires when the editor is first receives the focus. Any insertion must wait
25828              * until after this event.
25829              * @param {HtmlEditor} this
25830              */
25831             activate: true,
25832              /**
25833              * @event beforesync
25834              * Fires before the textarea is updated with content from the editor iframe. Return false
25835              * to cancel the sync.
25836              * @param {HtmlEditor} this
25837              * @param {String} html
25838              */
25839             beforesync: true,
25840              /**
25841              * @event beforepush
25842              * Fires before the iframe editor is updated with content from the textarea. Return false
25843              * to cancel the push.
25844              * @param {HtmlEditor} this
25845              * @param {String} html
25846              */
25847             beforepush: true,
25848              /**
25849              * @event sync
25850              * Fires when the textarea is updated with content from the editor iframe.
25851              * @param {HtmlEditor} this
25852              * @param {String} html
25853              */
25854             sync: true,
25855              /**
25856              * @event push
25857              * Fires when the iframe editor is updated with content from the textarea.
25858              * @param {HtmlEditor} this
25859              * @param {String} html
25860              */
25861             push: true,
25862              /**
25863              * @event editmodechange
25864              * Fires when the editor switches edit modes
25865              * @param {HtmlEditor} this
25866              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25867              */
25868             editmodechange: true,
25869             /**
25870              * @event editorevent
25871              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25872              * @param {HtmlEditor} this
25873              */
25874             editorevent: true,
25875             /**
25876              * @event firstfocus
25877              * Fires when on first focus - needed by toolbars..
25878              * @param {HtmlEditor} this
25879              */
25880             firstfocus: true,
25881             /**
25882              * @event autosave
25883              * Auto save the htmlEditor value as a file into Events
25884              * @param {HtmlEditor} this
25885              */
25886             autosave: true,
25887             /**
25888              * @event savedpreview
25889              * preview the saved version of htmlEditor
25890              * @param {HtmlEditor} this
25891              */
25892             savedpreview: true
25893         });
25894 };
25895
25896
25897 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25898     
25899     
25900       /**
25901      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25902      */
25903     toolbars : false,
25904     
25905      /**
25906     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25907     */
25908     btns : [],
25909    
25910      /**
25911      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25912      *                        Roo.resizable.
25913      */
25914     resizable : false,
25915      /**
25916      * @cfg {Number} height (in pixels)
25917      */   
25918     height: 300,
25919    /**
25920      * @cfg {Number} width (in pixels)
25921      */   
25922     width: false,
25923     
25924     /**
25925      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25926      * 
25927      */
25928     stylesheets: false,
25929     
25930     // id of frame..
25931     frameId: false,
25932     
25933     // private properties
25934     validationEvent : false,
25935     deferHeight: true,
25936     initialized : false,
25937     activated : false,
25938     
25939     onFocus : Roo.emptyFn,
25940     iframePad:3,
25941     hideMode:'offsets',
25942     
25943     tbContainer : false,
25944     
25945     bodyCls : '',
25946     
25947     toolbarContainer :function() {
25948         return this.wrap.select('.x-html-editor-tb',true).first();
25949     },
25950
25951     /**
25952      * Protected method that will not generally be called directly. It
25953      * is called when the editor creates its toolbar. Override this method if you need to
25954      * add custom toolbar buttons.
25955      * @param {HtmlEditor} editor
25956      */
25957     createToolbar : function(){
25958         Roo.log('renewing');
25959         Roo.log("create toolbars");
25960         
25961         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25962         this.toolbars[0].render(this.toolbarContainer());
25963         
25964         return;
25965         
25966 //        if (!editor.toolbars || !editor.toolbars.length) {
25967 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25968 //        }
25969 //        
25970 //        for (var i =0 ; i < editor.toolbars.length;i++) {
25971 //            editor.toolbars[i] = Roo.factory(
25972 //                    typeof(editor.toolbars[i]) == 'string' ?
25973 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
25974 //                Roo.bootstrap.HtmlEditor);
25975 //            editor.toolbars[i].init(editor);
25976 //        }
25977     },
25978
25979      
25980     // private
25981     onRender : function(ct, position)
25982     {
25983        // Roo.log("Call onRender: " + this.xtype);
25984         var _t = this;
25985         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25986       
25987         this.wrap = this.inputEl().wrap({
25988             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25989         });
25990         
25991         this.editorcore.onRender(ct, position);
25992          
25993         if (this.resizable) {
25994             this.resizeEl = new Roo.Resizable(this.wrap, {
25995                 pinned : true,
25996                 wrap: true,
25997                 dynamic : true,
25998                 minHeight : this.height,
25999                 height: this.height,
26000                 handles : this.resizable,
26001                 width: this.width,
26002                 listeners : {
26003                     resize : function(r, w, h) {
26004                         _t.onResize(w,h); // -something
26005                     }
26006                 }
26007             });
26008             
26009         }
26010         this.createToolbar(this);
26011        
26012         
26013         if(!this.width && this.resizable){
26014             this.setSize(this.wrap.getSize());
26015         }
26016         if (this.resizeEl) {
26017             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26018             // should trigger onReize..
26019         }
26020         
26021     },
26022
26023     // private
26024     onResize : function(w, h)
26025     {
26026         Roo.log('resize: ' +w + ',' + h );
26027         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26028         var ew = false;
26029         var eh = false;
26030         
26031         if(this.inputEl() ){
26032             if(typeof w == 'number'){
26033                 var aw = w - this.wrap.getFrameWidth('lr');
26034                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26035                 ew = aw;
26036             }
26037             if(typeof h == 'number'){
26038                  var tbh = -11;  // fixme it needs to tool bar size!
26039                 for (var i =0; i < this.toolbars.length;i++) {
26040                     // fixme - ask toolbars for heights?
26041                     tbh += this.toolbars[i].el.getHeight();
26042                     //if (this.toolbars[i].footer) {
26043                     //    tbh += this.toolbars[i].footer.el.getHeight();
26044                     //}
26045                 }
26046               
26047                 
26048                 
26049                 
26050                 
26051                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26052                 ah -= 5; // knock a few pixes off for look..
26053                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26054                 var eh = ah;
26055             }
26056         }
26057         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26058         this.editorcore.onResize(ew,eh);
26059         
26060     },
26061
26062     /**
26063      * Toggles the editor between standard and source edit mode.
26064      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26065      */
26066     toggleSourceEdit : function(sourceEditMode)
26067     {
26068         this.editorcore.toggleSourceEdit(sourceEditMode);
26069         
26070         if(this.editorcore.sourceEditMode){
26071             Roo.log('editor - showing textarea');
26072             
26073 //            Roo.log('in');
26074 //            Roo.log(this.syncValue());
26075             this.syncValue();
26076             this.inputEl().removeClass(['hide', 'x-hidden']);
26077             this.inputEl().dom.removeAttribute('tabIndex');
26078             this.inputEl().focus();
26079         }else{
26080             Roo.log('editor - hiding textarea');
26081 //            Roo.log('out')
26082 //            Roo.log(this.pushValue()); 
26083             this.pushValue();
26084             
26085             this.inputEl().addClass(['hide', 'x-hidden']);
26086             this.inputEl().dom.setAttribute('tabIndex', -1);
26087             //this.deferFocus();
26088         }
26089          
26090         if(this.resizable){
26091             this.setSize(this.wrap.getSize());
26092         }
26093         
26094         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26095     },
26096  
26097     // private (for BoxComponent)
26098     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26099
26100     // private (for BoxComponent)
26101     getResizeEl : function(){
26102         return this.wrap;
26103     },
26104
26105     // private (for BoxComponent)
26106     getPositionEl : function(){
26107         return this.wrap;
26108     },
26109
26110     // private
26111     initEvents : function(){
26112         this.originalValue = this.getValue();
26113     },
26114
26115 //    /**
26116 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26117 //     * @method
26118 //     */
26119 //    markInvalid : Roo.emptyFn,
26120 //    /**
26121 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26122 //     * @method
26123 //     */
26124 //    clearInvalid : Roo.emptyFn,
26125
26126     setValue : function(v){
26127         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26128         this.editorcore.pushValue();
26129     },
26130
26131      
26132     // private
26133     deferFocus : function(){
26134         this.focus.defer(10, this);
26135     },
26136
26137     // doc'ed in Field
26138     focus : function(){
26139         this.editorcore.focus();
26140         
26141     },
26142       
26143
26144     // private
26145     onDestroy : function(){
26146         
26147         
26148         
26149         if(this.rendered){
26150             
26151             for (var i =0; i < this.toolbars.length;i++) {
26152                 // fixme - ask toolbars for heights?
26153                 this.toolbars[i].onDestroy();
26154             }
26155             
26156             this.wrap.dom.innerHTML = '';
26157             this.wrap.remove();
26158         }
26159     },
26160
26161     // private
26162     onFirstFocus : function(){
26163         //Roo.log("onFirstFocus");
26164         this.editorcore.onFirstFocus();
26165          for (var i =0; i < this.toolbars.length;i++) {
26166             this.toolbars[i].onFirstFocus();
26167         }
26168         
26169     },
26170     
26171     // private
26172     syncValue : function()
26173     {   
26174         this.editorcore.syncValue();
26175     },
26176     
26177     pushValue : function()
26178     {   
26179         this.editorcore.pushValue();
26180     }
26181      
26182     
26183     // hide stuff that is not compatible
26184     /**
26185      * @event blur
26186      * @hide
26187      */
26188     /**
26189      * @event change
26190      * @hide
26191      */
26192     /**
26193      * @event focus
26194      * @hide
26195      */
26196     /**
26197      * @event specialkey
26198      * @hide
26199      */
26200     /**
26201      * @cfg {String} fieldClass @hide
26202      */
26203     /**
26204      * @cfg {String} focusClass @hide
26205      */
26206     /**
26207      * @cfg {String} autoCreate @hide
26208      */
26209     /**
26210      * @cfg {String} inputType @hide
26211      */
26212      
26213     /**
26214      * @cfg {String} invalidText @hide
26215      */
26216     /**
26217      * @cfg {String} msgFx @hide
26218      */
26219     /**
26220      * @cfg {String} validateOnBlur @hide
26221      */
26222 });
26223  
26224     
26225    
26226    
26227    
26228       
26229 Roo.namespace('Roo.bootstrap.htmleditor');
26230 /**
26231  * @class Roo.bootstrap.HtmlEditorToolbar1
26232  * Basic Toolbar
26233  * 
26234  * @example
26235  * Usage:
26236  *
26237  new Roo.bootstrap.HtmlEditor({
26238     ....
26239     toolbars : [
26240         new Roo.bootstrap.HtmlEditorToolbar1({
26241             disable : { fonts: 1 , format: 1, ..., ... , ...],
26242             btns : [ .... ]
26243         })
26244     }
26245      
26246  * 
26247  * @cfg {Object} disable List of elements to disable..
26248  * @cfg {Array} btns List of additional buttons.
26249  * 
26250  * 
26251  * NEEDS Extra CSS? 
26252  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26253  */
26254  
26255 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26256 {
26257     
26258     Roo.apply(this, config);
26259     
26260     // default disabled, based on 'good practice'..
26261     this.disable = this.disable || {};
26262     Roo.applyIf(this.disable, {
26263         fontSize : true,
26264         colors : true,
26265         specialElements : true
26266     });
26267     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26268     
26269     this.editor = config.editor;
26270     this.editorcore = config.editor.editorcore;
26271     
26272     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26273     
26274     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26275     // dont call parent... till later.
26276 }
26277 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26278      
26279     bar : true,
26280     
26281     editor : false,
26282     editorcore : false,
26283     
26284     
26285     formats : [
26286         "p" ,  
26287         "h1","h2","h3","h4","h5","h6", 
26288         "pre", "code", 
26289         "abbr", "acronym", "address", "cite", "samp", "var",
26290         'div','span'
26291     ],
26292     
26293     onRender : function(ct, position)
26294     {
26295        // Roo.log("Call onRender: " + this.xtype);
26296         
26297        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26298        Roo.log(this.el);
26299        this.el.dom.style.marginBottom = '0';
26300        var _this = this;
26301        var editorcore = this.editorcore;
26302        var editor= this.editor;
26303        
26304        var children = [];
26305        var btn = function(id,cmd , toggle, handler, html){
26306        
26307             var  event = toggle ? 'toggle' : 'click';
26308        
26309             var a = {
26310                 size : 'sm',
26311                 xtype: 'Button',
26312                 xns: Roo.bootstrap,
26313                 //glyphicon : id,
26314                 fa: id,
26315                 cmd : id || cmd,
26316                 enableToggle:toggle !== false,
26317                 html : html || '',
26318                 pressed : toggle ? false : null,
26319                 listeners : {}
26320             };
26321             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26322                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26323             };
26324             children.push(a);
26325             return a;
26326        }
26327        
26328     //    var cb_box = function...
26329         
26330         var style = {
26331                 xtype: 'Button',
26332                 size : 'sm',
26333                 xns: Roo.bootstrap,
26334                 fa : 'font',
26335                 //html : 'submit'
26336                 menu : {
26337                     xtype: 'Menu',
26338                     xns: Roo.bootstrap,
26339                     items:  []
26340                 }
26341         };
26342         Roo.each(this.formats, function(f) {
26343             style.menu.items.push({
26344                 xtype :'MenuItem',
26345                 xns: Roo.bootstrap,
26346                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26347                 tagname : f,
26348                 listeners : {
26349                     click : function()
26350                     {
26351                         editorcore.insertTag(this.tagname);
26352                         editor.focus();
26353                     }
26354                 }
26355                 
26356             });
26357         });
26358         children.push(style);   
26359         
26360         btn('bold',false,true);
26361         btn('italic',false,true);
26362         btn('align-left', 'justifyleft',true);
26363         btn('align-center', 'justifycenter',true);
26364         btn('align-right' , 'justifyright',true);
26365         btn('link', false, false, function(btn) {
26366             //Roo.log("create link?");
26367             var url = prompt(this.createLinkText, this.defaultLinkValue);
26368             if(url && url != 'http:/'+'/'){
26369                 this.editorcore.relayCmd('createlink', url);
26370             }
26371         }),
26372         btn('list','insertunorderedlist',true);
26373         btn('pencil', false,true, function(btn){
26374                 Roo.log(this);
26375                 this.toggleSourceEdit(btn.pressed);
26376         });
26377         
26378         if (this.editor.btns.length > 0) {
26379             for (var i = 0; i<this.editor.btns.length; i++) {
26380                 children.push(this.editor.btns[i]);
26381             }
26382         }
26383         
26384         /*
26385         var cog = {
26386                 xtype: 'Button',
26387                 size : 'sm',
26388                 xns: Roo.bootstrap,
26389                 glyphicon : 'cog',
26390                 //html : 'submit'
26391                 menu : {
26392                     xtype: 'Menu',
26393                     xns: Roo.bootstrap,
26394                     items:  []
26395                 }
26396         };
26397         
26398         cog.menu.items.push({
26399             xtype :'MenuItem',
26400             xns: Roo.bootstrap,
26401             html : Clean styles,
26402             tagname : f,
26403             listeners : {
26404                 click : function()
26405                 {
26406                     editorcore.insertTag(this.tagname);
26407                     editor.focus();
26408                 }
26409             }
26410             
26411         });
26412        */
26413         
26414          
26415        this.xtype = 'NavSimplebar';
26416         
26417         for(var i=0;i< children.length;i++) {
26418             
26419             this.buttons.add(this.addxtypeChild(children[i]));
26420             
26421         }
26422         
26423         editor.on('editorevent', this.updateToolbar, this);
26424     },
26425     onBtnClick : function(id)
26426     {
26427        this.editorcore.relayCmd(id);
26428        this.editorcore.focus();
26429     },
26430     
26431     /**
26432      * Protected method that will not generally be called directly. It triggers
26433      * a toolbar update by reading the markup state of the current selection in the editor.
26434      */
26435     updateToolbar: function(){
26436
26437         if(!this.editorcore.activated){
26438             this.editor.onFirstFocus(); // is this neeed?
26439             return;
26440         }
26441
26442         var btns = this.buttons; 
26443         var doc = this.editorcore.doc;
26444         btns.get('bold').setActive(doc.queryCommandState('bold'));
26445         btns.get('italic').setActive(doc.queryCommandState('italic'));
26446         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26447         
26448         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26449         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26450         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26451         
26452         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26453         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26454          /*
26455         
26456         var ans = this.editorcore.getAllAncestors();
26457         if (this.formatCombo) {
26458             
26459             
26460             var store = this.formatCombo.store;
26461             this.formatCombo.setValue("");
26462             for (var i =0; i < ans.length;i++) {
26463                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26464                     // select it..
26465                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26466                     break;
26467                 }
26468             }
26469         }
26470         
26471         
26472         
26473         // hides menus... - so this cant be on a menu...
26474         Roo.bootstrap.MenuMgr.hideAll();
26475         */
26476         Roo.bootstrap.MenuMgr.hideAll();
26477         //this.editorsyncValue();
26478     },
26479     onFirstFocus: function() {
26480         this.buttons.each(function(item){
26481            item.enable();
26482         });
26483     },
26484     toggleSourceEdit : function(sourceEditMode){
26485         
26486           
26487         if(sourceEditMode){
26488             Roo.log("disabling buttons");
26489            this.buttons.each( function(item){
26490                 if(item.cmd != 'pencil'){
26491                     item.disable();
26492                 }
26493             });
26494           
26495         }else{
26496             Roo.log("enabling buttons");
26497             if(this.editorcore.initialized){
26498                 this.buttons.each( function(item){
26499                     item.enable();
26500                 });
26501             }
26502             
26503         }
26504         Roo.log("calling toggole on editor");
26505         // tell the editor that it's been pressed..
26506         this.editor.toggleSourceEdit(sourceEditMode);
26507        
26508     }
26509 });
26510
26511
26512
26513
26514  
26515 /*
26516  * - LGPL
26517  */
26518
26519 /**
26520  * @class Roo.bootstrap.Markdown
26521  * @extends Roo.bootstrap.TextArea
26522  * Bootstrap Showdown editable area
26523  * @cfg {string} content
26524  * 
26525  * @constructor
26526  * Create a new Showdown
26527  */
26528
26529 Roo.bootstrap.Markdown = function(config){
26530     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26531    
26532 };
26533
26534 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26535     
26536     editing :false,
26537     
26538     initEvents : function()
26539     {
26540         
26541         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26542         this.markdownEl = this.el.createChild({
26543             cls : 'roo-markdown-area'
26544         });
26545         this.inputEl().addClass('d-none');
26546         if (this.getValue() == '') {
26547             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26548             
26549         } else {
26550             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26551         }
26552         this.markdownEl.on('click', this.toggleTextEdit, this);
26553         this.on('blur', this.toggleTextEdit, this);
26554         this.on('specialkey', this.resizeTextArea, this);
26555     },
26556     
26557     toggleTextEdit : function()
26558     {
26559         var sh = this.markdownEl.getHeight();
26560         this.inputEl().addClass('d-none');
26561         this.markdownEl.addClass('d-none');
26562         if (!this.editing) {
26563             // show editor?
26564             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26565             this.inputEl().removeClass('d-none');
26566             this.inputEl().focus();
26567             this.editing = true;
26568             return;
26569         }
26570         // show showdown...
26571         this.updateMarkdown();
26572         this.markdownEl.removeClass('d-none');
26573         this.editing = false;
26574         return;
26575     },
26576     updateMarkdown : function()
26577     {
26578         if (this.getValue() == '') {
26579             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26580             return;
26581         }
26582  
26583         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26584     },
26585     
26586     resizeTextArea: function () {
26587         
26588         var sh = 100;
26589         Roo.log([sh, this.getValue().split("\n").length * 30]);
26590         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26591     },
26592     setValue : function(val)
26593     {
26594         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26595         if (!this.editing) {
26596             this.updateMarkdown();
26597         }
26598         
26599     },
26600     focus : function()
26601     {
26602         if (!this.editing) {
26603             this.toggleTextEdit();
26604         }
26605         
26606     }
26607
26608
26609 });
26610 /**
26611  * @class Roo.bootstrap.Table.AbstractSelectionModel
26612  * @extends Roo.util.Observable
26613  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26614  * implemented by descendant classes.  This class should not be directly instantiated.
26615  * @constructor
26616  */
26617 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26618     this.locked = false;
26619     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26620 };
26621
26622
26623 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26624     /** @ignore Called by the grid automatically. Do not call directly. */
26625     init : function(grid){
26626         this.grid = grid;
26627         this.initEvents();
26628     },
26629
26630     /**
26631      * Locks the selections.
26632      */
26633     lock : function(){
26634         this.locked = true;
26635     },
26636
26637     /**
26638      * Unlocks the selections.
26639      */
26640     unlock : function(){
26641         this.locked = false;
26642     },
26643
26644     /**
26645      * Returns true if the selections are locked.
26646      * @return {Boolean}
26647      */
26648     isLocked : function(){
26649         return this.locked;
26650     },
26651     
26652     
26653     initEvents : function ()
26654     {
26655         
26656     }
26657 });
26658 /**
26659  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26660  * @class Roo.bootstrap.Table.RowSelectionModel
26661  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26662  * It supports multiple selections and keyboard selection/navigation. 
26663  * @constructor
26664  * @param {Object} config
26665  */
26666
26667 Roo.bootstrap.Table.RowSelectionModel = function(config){
26668     Roo.apply(this, config);
26669     this.selections = new Roo.util.MixedCollection(false, function(o){
26670         return o.id;
26671     });
26672
26673     this.last = false;
26674     this.lastActive = false;
26675
26676     this.addEvents({
26677         /**
26678              * @event selectionchange
26679              * Fires when the selection changes
26680              * @param {SelectionModel} this
26681              */
26682             "selectionchange" : true,
26683         /**
26684              * @event afterselectionchange
26685              * Fires after the selection changes (eg. by key press or clicking)
26686              * @param {SelectionModel} this
26687              */
26688             "afterselectionchange" : true,
26689         /**
26690              * @event beforerowselect
26691              * Fires when a row is selected being selected, return false to cancel.
26692              * @param {SelectionModel} this
26693              * @param {Number} rowIndex The selected index
26694              * @param {Boolean} keepExisting False if other selections will be cleared
26695              */
26696             "beforerowselect" : true,
26697         /**
26698              * @event rowselect
26699              * Fires when a row is selected.
26700              * @param {SelectionModel} this
26701              * @param {Number} rowIndex The selected index
26702              * @param {Roo.data.Record} r The record
26703              */
26704             "rowselect" : true,
26705         /**
26706              * @event rowdeselect
26707              * Fires when a row is deselected.
26708              * @param {SelectionModel} this
26709              * @param {Number} rowIndex The selected index
26710              */
26711         "rowdeselect" : true
26712     });
26713     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26714     this.locked = false;
26715  };
26716
26717 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26718     /**
26719      * @cfg {Boolean} singleSelect
26720      * True to allow selection of only one row at a time (defaults to false)
26721      */
26722     singleSelect : false,
26723
26724     // private
26725     initEvents : function()
26726     {
26727
26728         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26729         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26730         //}else{ // allow click to work like normal
26731          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26732         //}
26733         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26734         this.grid.on("rowclick", this.handleMouseDown, this);
26735         
26736         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26737             "up" : function(e){
26738                 if(!e.shiftKey){
26739                     this.selectPrevious(e.shiftKey);
26740                 }else if(this.last !== false && this.lastActive !== false){
26741                     var last = this.last;
26742                     this.selectRange(this.last,  this.lastActive-1);
26743                     this.grid.getView().focusRow(this.lastActive);
26744                     if(last !== false){
26745                         this.last = last;
26746                     }
26747                 }else{
26748                     this.selectFirstRow();
26749                 }
26750                 this.fireEvent("afterselectionchange", this);
26751             },
26752             "down" : function(e){
26753                 if(!e.shiftKey){
26754                     this.selectNext(e.shiftKey);
26755                 }else if(this.last !== false && this.lastActive !== false){
26756                     var last = this.last;
26757                     this.selectRange(this.last,  this.lastActive+1);
26758                     this.grid.getView().focusRow(this.lastActive);
26759                     if(last !== false){
26760                         this.last = last;
26761                     }
26762                 }else{
26763                     this.selectFirstRow();
26764                 }
26765                 this.fireEvent("afterselectionchange", this);
26766             },
26767             scope: this
26768         });
26769         this.grid.store.on('load', function(){
26770             this.selections.clear();
26771         },this);
26772         /*
26773         var view = this.grid.view;
26774         view.on("refresh", this.onRefresh, this);
26775         view.on("rowupdated", this.onRowUpdated, this);
26776         view.on("rowremoved", this.onRemove, this);
26777         */
26778     },
26779
26780     // private
26781     onRefresh : function()
26782     {
26783         var ds = this.grid.store, i, v = this.grid.view;
26784         var s = this.selections;
26785         s.each(function(r){
26786             if((i = ds.indexOfId(r.id)) != -1){
26787                 v.onRowSelect(i);
26788             }else{
26789                 s.remove(r);
26790             }
26791         });
26792     },
26793
26794     // private
26795     onRemove : function(v, index, r){
26796         this.selections.remove(r);
26797     },
26798
26799     // private
26800     onRowUpdated : function(v, index, r){
26801         if(this.isSelected(r)){
26802             v.onRowSelect(index);
26803         }
26804     },
26805
26806     /**
26807      * Select records.
26808      * @param {Array} records The records to select
26809      * @param {Boolean} keepExisting (optional) True to keep existing selections
26810      */
26811     selectRecords : function(records, keepExisting)
26812     {
26813         if(!keepExisting){
26814             this.clearSelections();
26815         }
26816             var ds = this.grid.store;
26817         for(var i = 0, len = records.length; i < len; i++){
26818             this.selectRow(ds.indexOf(records[i]), true);
26819         }
26820     },
26821
26822     /**
26823      * Gets the number of selected rows.
26824      * @return {Number}
26825      */
26826     getCount : function(){
26827         return this.selections.length;
26828     },
26829
26830     /**
26831      * Selects the first row in the grid.
26832      */
26833     selectFirstRow : function(){
26834         this.selectRow(0);
26835     },
26836
26837     /**
26838      * Select the last row.
26839      * @param {Boolean} keepExisting (optional) True to keep existing selections
26840      */
26841     selectLastRow : function(keepExisting){
26842         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26843         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26844     },
26845
26846     /**
26847      * Selects the row immediately following the last selected row.
26848      * @param {Boolean} keepExisting (optional) True to keep existing selections
26849      */
26850     selectNext : function(keepExisting)
26851     {
26852             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26853             this.selectRow(this.last+1, keepExisting);
26854             this.grid.getView().focusRow(this.last);
26855         }
26856     },
26857
26858     /**
26859      * Selects the row that precedes the last selected row.
26860      * @param {Boolean} keepExisting (optional) True to keep existing selections
26861      */
26862     selectPrevious : function(keepExisting){
26863         if(this.last){
26864             this.selectRow(this.last-1, keepExisting);
26865             this.grid.getView().focusRow(this.last);
26866         }
26867     },
26868
26869     /**
26870      * Returns the selected records
26871      * @return {Array} Array of selected records
26872      */
26873     getSelections : function(){
26874         return [].concat(this.selections.items);
26875     },
26876
26877     /**
26878      * Returns the first selected record.
26879      * @return {Record}
26880      */
26881     getSelected : function(){
26882         return this.selections.itemAt(0);
26883     },
26884
26885
26886     /**
26887      * Clears all selections.
26888      */
26889     clearSelections : function(fast)
26890     {
26891         if(this.locked) {
26892             return;
26893         }
26894         if(fast !== true){
26895                 var ds = this.grid.store;
26896             var s = this.selections;
26897             s.each(function(r){
26898                 this.deselectRow(ds.indexOfId(r.id));
26899             }, this);
26900             s.clear();
26901         }else{
26902             this.selections.clear();
26903         }
26904         this.last = false;
26905     },
26906
26907
26908     /**
26909      * Selects all rows.
26910      */
26911     selectAll : function(){
26912         if(this.locked) {
26913             return;
26914         }
26915         this.selections.clear();
26916         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26917             this.selectRow(i, true);
26918         }
26919     },
26920
26921     /**
26922      * Returns True if there is a selection.
26923      * @return {Boolean}
26924      */
26925     hasSelection : function(){
26926         return this.selections.length > 0;
26927     },
26928
26929     /**
26930      * Returns True if the specified row is selected.
26931      * @param {Number/Record} record The record or index of the record to check
26932      * @return {Boolean}
26933      */
26934     isSelected : function(index){
26935             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26936         return (r && this.selections.key(r.id) ? true : false);
26937     },
26938
26939     /**
26940      * Returns True if the specified record id is selected.
26941      * @param {String} id The id of record to check
26942      * @return {Boolean}
26943      */
26944     isIdSelected : function(id){
26945         return (this.selections.key(id) ? true : false);
26946     },
26947
26948
26949     // private
26950     handleMouseDBClick : function(e, t){
26951         
26952     },
26953     // private
26954     handleMouseDown : function(e, t)
26955     {
26956             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26957         if(this.isLocked() || rowIndex < 0 ){
26958             return;
26959         };
26960         if(e.shiftKey && this.last !== false){
26961             var last = this.last;
26962             this.selectRange(last, rowIndex, e.ctrlKey);
26963             this.last = last; // reset the last
26964             t.focus();
26965     
26966         }else{
26967             var isSelected = this.isSelected(rowIndex);
26968             //Roo.log("select row:" + rowIndex);
26969             if(isSelected){
26970                 this.deselectRow(rowIndex);
26971             } else {
26972                         this.selectRow(rowIndex, true);
26973             }
26974     
26975             /*
26976                 if(e.button !== 0 && isSelected){
26977                 alert('rowIndex 2: ' + rowIndex);
26978                     view.focusRow(rowIndex);
26979                 }else if(e.ctrlKey && isSelected){
26980                     this.deselectRow(rowIndex);
26981                 }else if(!isSelected){
26982                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26983                     view.focusRow(rowIndex);
26984                 }
26985             */
26986         }
26987         this.fireEvent("afterselectionchange", this);
26988     },
26989     // private
26990     handleDragableRowClick :  function(grid, rowIndex, e) 
26991     {
26992         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26993             this.selectRow(rowIndex, false);
26994             grid.view.focusRow(rowIndex);
26995              this.fireEvent("afterselectionchange", this);
26996         }
26997     },
26998     
26999     /**
27000      * Selects multiple rows.
27001      * @param {Array} rows Array of the indexes of the row to select
27002      * @param {Boolean} keepExisting (optional) True to keep existing selections
27003      */
27004     selectRows : function(rows, keepExisting){
27005         if(!keepExisting){
27006             this.clearSelections();
27007         }
27008         for(var i = 0, len = rows.length; i < len; i++){
27009             this.selectRow(rows[i], true);
27010         }
27011     },
27012
27013     /**
27014      * Selects a range of rows. All rows in between startRow and endRow are also selected.
27015      * @param {Number} startRow The index of the first row in the range
27016      * @param {Number} endRow The index of the last row in the range
27017      * @param {Boolean} keepExisting (optional) True to retain existing selections
27018      */
27019     selectRange : function(startRow, endRow, keepExisting){
27020         if(this.locked) {
27021             return;
27022         }
27023         if(!keepExisting){
27024             this.clearSelections();
27025         }
27026         if(startRow <= endRow){
27027             for(var i = startRow; i <= endRow; i++){
27028                 this.selectRow(i, true);
27029             }
27030         }else{
27031             for(var i = startRow; i >= endRow; i--){
27032                 this.selectRow(i, true);
27033             }
27034         }
27035     },
27036
27037     /**
27038      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27039      * @param {Number} startRow The index of the first row in the range
27040      * @param {Number} endRow The index of the last row in the range
27041      */
27042     deselectRange : function(startRow, endRow, preventViewNotify){
27043         if(this.locked) {
27044             return;
27045         }
27046         for(var i = startRow; i <= endRow; i++){
27047             this.deselectRow(i, preventViewNotify);
27048         }
27049     },
27050
27051     /**
27052      * Selects a row.
27053      * @param {Number} row The index of the row to select
27054      * @param {Boolean} keepExisting (optional) True to keep existing selections
27055      */
27056     selectRow : function(index, keepExisting, preventViewNotify)
27057     {
27058             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27059             return;
27060         }
27061         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27062             if(!keepExisting || this.singleSelect){
27063                 this.clearSelections();
27064             }
27065             
27066             var r = this.grid.store.getAt(index);
27067             //console.log('selectRow - record id :' + r.id);
27068             
27069             this.selections.add(r);
27070             this.last = this.lastActive = index;
27071             if(!preventViewNotify){
27072                 var proxy = new Roo.Element(
27073                                 this.grid.getRowDom(index)
27074                 );
27075                 proxy.addClass('bg-info info');
27076             }
27077             this.fireEvent("rowselect", this, index, r);
27078             this.fireEvent("selectionchange", this);
27079         }
27080     },
27081
27082     /**
27083      * Deselects a row.
27084      * @param {Number} row The index of the row to deselect
27085      */
27086     deselectRow : function(index, preventViewNotify)
27087     {
27088         if(this.locked) {
27089             return;
27090         }
27091         if(this.last == index){
27092             this.last = false;
27093         }
27094         if(this.lastActive == index){
27095             this.lastActive = false;
27096         }
27097         
27098         var r = this.grid.store.getAt(index);
27099         if (!r) {
27100             return;
27101         }
27102         
27103         this.selections.remove(r);
27104         //.console.log('deselectRow - record id :' + r.id);
27105         if(!preventViewNotify){
27106         
27107             var proxy = new Roo.Element(
27108                 this.grid.getRowDom(index)
27109             );
27110             proxy.removeClass('bg-info info');
27111         }
27112         this.fireEvent("rowdeselect", this, index);
27113         this.fireEvent("selectionchange", this);
27114     },
27115
27116     // private
27117     restoreLast : function(){
27118         if(this._last){
27119             this.last = this._last;
27120         }
27121     },
27122
27123     // private
27124     acceptsNav : function(row, col, cm){
27125         return !cm.isHidden(col) && cm.isCellEditable(col, row);
27126     },
27127
27128     // private
27129     onEditorKey : function(field, e){
27130         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27131         if(k == e.TAB){
27132             e.stopEvent();
27133             ed.completeEdit();
27134             if(e.shiftKey){
27135                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27136             }else{
27137                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27138             }
27139         }else if(k == e.ENTER && !e.ctrlKey){
27140             e.stopEvent();
27141             ed.completeEdit();
27142             if(e.shiftKey){
27143                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27144             }else{
27145                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27146             }
27147         }else if(k == e.ESC){
27148             ed.cancelEdit();
27149         }
27150         if(newCell){
27151             g.startEditing(newCell[0], newCell[1]);
27152         }
27153     }
27154 });
27155 /*
27156  * Based on:
27157  * Ext JS Library 1.1.1
27158  * Copyright(c) 2006-2007, Ext JS, LLC.
27159  *
27160  * Originally Released Under LGPL - original licence link has changed is not relivant.
27161  *
27162  * Fork - LGPL
27163  * <script type="text/javascript">
27164  */
27165  
27166 /**
27167  * @class Roo.bootstrap.PagingToolbar
27168  * @extends Roo.bootstrap.NavSimplebar
27169  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27170  * @constructor
27171  * Create a new PagingToolbar
27172  * @param {Object} config The config object
27173  * @param {Roo.data.Store} store
27174  */
27175 Roo.bootstrap.PagingToolbar = function(config)
27176 {
27177     // old args format still supported... - xtype is prefered..
27178         // created from xtype...
27179     
27180     this.ds = config.dataSource;
27181     
27182     if (config.store && !this.ds) {
27183         this.store= Roo.factory(config.store, Roo.data);
27184         this.ds = this.store;
27185         this.ds.xmodule = this.xmodule || false;
27186     }
27187     
27188     this.toolbarItems = [];
27189     if (config.items) {
27190         this.toolbarItems = config.items;
27191     }
27192     
27193     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27194     
27195     this.cursor = 0;
27196     
27197     if (this.ds) { 
27198         this.bind(this.ds);
27199     }
27200     
27201     if (Roo.bootstrap.version == 4) {
27202         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27203     } else {
27204         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27205     }
27206     
27207 };
27208
27209 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27210     /**
27211      * @cfg {Roo.data.Store} dataSource
27212      * The underlying data store providing the paged data
27213      */
27214     /**
27215      * @cfg {String/HTMLElement/Element} container
27216      * container The id or element that will contain the toolbar
27217      */
27218     /**
27219      * @cfg {Boolean} displayInfo
27220      * True to display the displayMsg (defaults to false)
27221      */
27222     /**
27223      * @cfg {Number} pageSize
27224      * The number of records to display per page (defaults to 20)
27225      */
27226     pageSize: 20,
27227     /**
27228      * @cfg {String} displayMsg
27229      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27230      */
27231     displayMsg : 'Displaying {0} - {1} of {2}',
27232     /**
27233      * @cfg {String} emptyMsg
27234      * The message to display when no records are found (defaults to "No data to display")
27235      */
27236     emptyMsg : 'No data to display',
27237     /**
27238      * Customizable piece of the default paging text (defaults to "Page")
27239      * @type String
27240      */
27241     beforePageText : "Page",
27242     /**
27243      * Customizable piece of the default paging text (defaults to "of %0")
27244      * @type String
27245      */
27246     afterPageText : "of {0}",
27247     /**
27248      * Customizable piece of the default paging text (defaults to "First Page")
27249      * @type String
27250      */
27251     firstText : "First Page",
27252     /**
27253      * Customizable piece of the default paging text (defaults to "Previous Page")
27254      * @type String
27255      */
27256     prevText : "Previous Page",
27257     /**
27258      * Customizable piece of the default paging text (defaults to "Next Page")
27259      * @type String
27260      */
27261     nextText : "Next Page",
27262     /**
27263      * Customizable piece of the default paging text (defaults to "Last Page")
27264      * @type String
27265      */
27266     lastText : "Last Page",
27267     /**
27268      * Customizable piece of the default paging text (defaults to "Refresh")
27269      * @type String
27270      */
27271     refreshText : "Refresh",
27272
27273     buttons : false,
27274     // private
27275     onRender : function(ct, position) 
27276     {
27277         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27278         this.navgroup.parentId = this.id;
27279         this.navgroup.onRender(this.el, null);
27280         // add the buttons to the navgroup
27281         
27282         if(this.displayInfo){
27283             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27284             this.displayEl = this.el.select('.x-paging-info', true).first();
27285 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27286 //            this.displayEl = navel.el.select('span',true).first();
27287         }
27288         
27289         var _this = this;
27290         
27291         if(this.buttons){
27292             Roo.each(_this.buttons, function(e){ // this might need to use render????
27293                Roo.factory(e).render(_this.el);
27294             });
27295         }
27296             
27297         Roo.each(_this.toolbarItems, function(e) {
27298             _this.navgroup.addItem(e);
27299         });
27300         
27301         
27302         this.first = this.navgroup.addItem({
27303             tooltip: this.firstText,
27304             cls: "prev btn-outline-secondary",
27305             html : ' <i class="fa fa-step-backward"></i>',
27306             disabled: true,
27307             preventDefault: true,
27308             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27309         });
27310         
27311         this.prev =  this.navgroup.addItem({
27312             tooltip: this.prevText,
27313             cls: "prev btn-outline-secondary",
27314             html : ' <i class="fa fa-backward"></i>',
27315             disabled: true,
27316             preventDefault: true,
27317             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27318         });
27319     //this.addSeparator();
27320         
27321         
27322         var field = this.navgroup.addItem( {
27323             tagtype : 'span',
27324             cls : 'x-paging-position  btn-outline-secondary',
27325              disabled: true,
27326             html : this.beforePageText  +
27327                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27328                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27329          } ); //?? escaped?
27330         
27331         this.field = field.el.select('input', true).first();
27332         this.field.on("keydown", this.onPagingKeydown, this);
27333         this.field.on("focus", function(){this.dom.select();});
27334     
27335     
27336         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27337         //this.field.setHeight(18);
27338         //this.addSeparator();
27339         this.next = this.navgroup.addItem({
27340             tooltip: this.nextText,
27341             cls: "next btn-outline-secondary",
27342             html : ' <i class="fa fa-forward"></i>',
27343             disabled: true,
27344             preventDefault: true,
27345             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27346         });
27347         this.last = this.navgroup.addItem({
27348             tooltip: this.lastText,
27349             html : ' <i class="fa fa-step-forward"></i>',
27350             cls: "next btn-outline-secondary",
27351             disabled: true,
27352             preventDefault: true,
27353             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27354         });
27355     //this.addSeparator();
27356         this.loading = this.navgroup.addItem({
27357             tooltip: this.refreshText,
27358             cls: "btn-outline-secondary",
27359             html : ' <i class="fa fa-refresh"></i>',
27360             preventDefault: true,
27361             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27362         });
27363         
27364     },
27365
27366     // private
27367     updateInfo : function(){
27368         if(this.displayEl){
27369             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27370             var msg = count == 0 ?
27371                 this.emptyMsg :
27372                 String.format(
27373                     this.displayMsg,
27374                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27375                 );
27376             this.displayEl.update(msg);
27377         }
27378     },
27379
27380     // private
27381     onLoad : function(ds, r, o)
27382     {
27383         this.cursor = o.params && o.params.start ? o.params.start : 0;
27384         
27385         var d = this.getPageData(),
27386             ap = d.activePage,
27387             ps = d.pages;
27388         
27389         
27390         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27391         this.field.dom.value = ap;
27392         this.first.setDisabled(ap == 1);
27393         this.prev.setDisabled(ap == 1);
27394         this.next.setDisabled(ap == ps);
27395         this.last.setDisabled(ap == ps);
27396         this.loading.enable();
27397         this.updateInfo();
27398     },
27399
27400     // private
27401     getPageData : function(){
27402         var total = this.ds.getTotalCount();
27403         return {
27404             total : total,
27405             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27406             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27407         };
27408     },
27409
27410     // private
27411     onLoadError : function(){
27412         this.loading.enable();
27413     },
27414
27415     // private
27416     onPagingKeydown : function(e){
27417         var k = e.getKey();
27418         var d = this.getPageData();
27419         if(k == e.RETURN){
27420             var v = this.field.dom.value, pageNum;
27421             if(!v || isNaN(pageNum = parseInt(v, 10))){
27422                 this.field.dom.value = d.activePage;
27423                 return;
27424             }
27425             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27426             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27427             e.stopEvent();
27428         }
27429         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))
27430         {
27431           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27432           this.field.dom.value = pageNum;
27433           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27434           e.stopEvent();
27435         }
27436         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27437         {
27438           var v = this.field.dom.value, pageNum; 
27439           var increment = (e.shiftKey) ? 10 : 1;
27440           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27441                 increment *= -1;
27442           }
27443           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27444             this.field.dom.value = d.activePage;
27445             return;
27446           }
27447           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27448           {
27449             this.field.dom.value = parseInt(v, 10) + increment;
27450             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27451             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27452           }
27453           e.stopEvent();
27454         }
27455     },
27456
27457     // private
27458     beforeLoad : function(){
27459         if(this.loading){
27460             this.loading.disable();
27461         }
27462     },
27463
27464     // private
27465     onClick : function(which){
27466         
27467         var ds = this.ds;
27468         if (!ds) {
27469             return;
27470         }
27471         
27472         switch(which){
27473             case "first":
27474                 ds.load({params:{start: 0, limit: this.pageSize}});
27475             break;
27476             case "prev":
27477                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27478             break;
27479             case "next":
27480                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27481             break;
27482             case "last":
27483                 var total = ds.getTotalCount();
27484                 var extra = total % this.pageSize;
27485                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27486                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27487             break;
27488             case "refresh":
27489                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27490             break;
27491         }
27492     },
27493
27494     /**
27495      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27496      * @param {Roo.data.Store} store The data store to unbind
27497      */
27498     unbind : function(ds){
27499         ds.un("beforeload", this.beforeLoad, this);
27500         ds.un("load", this.onLoad, this);
27501         ds.un("loadexception", this.onLoadError, this);
27502         ds.un("remove", this.updateInfo, this);
27503         ds.un("add", this.updateInfo, this);
27504         this.ds = undefined;
27505     },
27506
27507     /**
27508      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27509      * @param {Roo.data.Store} store The data store to bind
27510      */
27511     bind : function(ds){
27512         ds.on("beforeload", this.beforeLoad, this);
27513         ds.on("load", this.onLoad, this);
27514         ds.on("loadexception", this.onLoadError, this);
27515         ds.on("remove", this.updateInfo, this);
27516         ds.on("add", this.updateInfo, this);
27517         this.ds = ds;
27518     }
27519 });/*
27520  * - LGPL
27521  *
27522  * element
27523  * 
27524  */
27525
27526 /**
27527  * @class Roo.bootstrap.MessageBar
27528  * @extends Roo.bootstrap.Component
27529  * Bootstrap MessageBar class
27530  * @cfg {String} html contents of the MessageBar
27531  * @cfg {String} weight (info | success | warning | danger) default info
27532  * @cfg {String} beforeClass insert the bar before the given class
27533  * @cfg {Boolean} closable (true | false) default false
27534  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27535  * 
27536  * @constructor
27537  * Create a new Element
27538  * @param {Object} config The config object
27539  */
27540
27541 Roo.bootstrap.MessageBar = function(config){
27542     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27543 };
27544
27545 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27546     
27547     html: '',
27548     weight: 'info',
27549     closable: false,
27550     fixed: false,
27551     beforeClass: 'bootstrap-sticky-wrap',
27552     
27553     getAutoCreate : function(){
27554         
27555         var cfg = {
27556             tag: 'div',
27557             cls: 'alert alert-dismissable alert-' + this.weight,
27558             cn: [
27559                 {
27560                     tag: 'span',
27561                     cls: 'message',
27562                     html: this.html || ''
27563                 }
27564             ]
27565         };
27566         
27567         if(this.fixed){
27568             cfg.cls += ' alert-messages-fixed';
27569         }
27570         
27571         if(this.closable){
27572             cfg.cn.push({
27573                 tag: 'button',
27574                 cls: 'close',
27575                 html: 'x'
27576             });
27577         }
27578         
27579         return cfg;
27580     },
27581     
27582     onRender : function(ct, position)
27583     {
27584         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27585         
27586         if(!this.el){
27587             var cfg = Roo.apply({},  this.getAutoCreate());
27588             cfg.id = Roo.id();
27589             
27590             if (this.cls) {
27591                 cfg.cls += ' ' + this.cls;
27592             }
27593             if (this.style) {
27594                 cfg.style = this.style;
27595             }
27596             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27597             
27598             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27599         }
27600         
27601         this.el.select('>button.close').on('click', this.hide, this);
27602         
27603     },
27604     
27605     show : function()
27606     {
27607         if (!this.rendered) {
27608             this.render();
27609         }
27610         
27611         this.el.show();
27612         
27613         this.fireEvent('show', this);
27614         
27615     },
27616     
27617     hide : function()
27618     {
27619         if (!this.rendered) {
27620             this.render();
27621         }
27622         
27623         this.el.hide();
27624         
27625         this.fireEvent('hide', this);
27626     },
27627     
27628     update : function()
27629     {
27630 //        var e = this.el.dom.firstChild;
27631 //        
27632 //        if(this.closable){
27633 //            e = e.nextSibling;
27634 //        }
27635 //        
27636 //        e.data = this.html || '';
27637
27638         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27639     }
27640    
27641 });
27642
27643  
27644
27645      /*
27646  * - LGPL
27647  *
27648  * Graph
27649  * 
27650  */
27651
27652
27653 /**
27654  * @class Roo.bootstrap.Graph
27655  * @extends Roo.bootstrap.Component
27656  * Bootstrap Graph class
27657 > Prameters
27658  -sm {number} sm 4
27659  -md {number} md 5
27660  @cfg {String} graphtype  bar | vbar | pie
27661  @cfg {number} g_x coodinator | centre x (pie)
27662  @cfg {number} g_y coodinator | centre y (pie)
27663  @cfg {number} g_r radius (pie)
27664  @cfg {number} g_height height of the chart (respected by all elements in the set)
27665  @cfg {number} g_width width of the chart (respected by all elements in the set)
27666  @cfg {Object} title The title of the chart
27667     
27668  -{Array}  values
27669  -opts (object) options for the chart 
27670      o {
27671      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27672      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27673      o vgutter (number)
27674      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.
27675      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27676      o to
27677      o stretch (boolean)
27678      o }
27679  -opts (object) options for the pie
27680      o{
27681      o cut
27682      o startAngle (number)
27683      o endAngle (number)
27684      } 
27685  *
27686  * @constructor
27687  * Create a new Input
27688  * @param {Object} config The config object
27689  */
27690
27691 Roo.bootstrap.Graph = function(config){
27692     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27693     
27694     this.addEvents({
27695         // img events
27696         /**
27697          * @event click
27698          * The img click event for the img.
27699          * @param {Roo.EventObject} e
27700          */
27701         "click" : true
27702     });
27703 };
27704
27705 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27706     
27707     sm: 4,
27708     md: 5,
27709     graphtype: 'bar',
27710     g_height: 250,
27711     g_width: 400,
27712     g_x: 50,
27713     g_y: 50,
27714     g_r: 30,
27715     opts:{
27716         //g_colors: this.colors,
27717         g_type: 'soft',
27718         g_gutter: '20%'
27719
27720     },
27721     title : false,
27722
27723     getAutoCreate : function(){
27724         
27725         var cfg = {
27726             tag: 'div',
27727             html : null
27728         };
27729         
27730         
27731         return  cfg;
27732     },
27733
27734     onRender : function(ct,position){
27735         
27736         
27737         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27738         
27739         if (typeof(Raphael) == 'undefined') {
27740             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27741             return;
27742         }
27743         
27744         this.raphael = Raphael(this.el.dom);
27745         
27746                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27747                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27748                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27749                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27750                 /*
27751                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27752                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27753                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27754                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27755                 
27756                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27757                 r.barchart(330, 10, 300, 220, data1);
27758                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27759                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27760                 */
27761                 
27762                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27763                 // r.barchart(30, 30, 560, 250,  xdata, {
27764                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27765                 //     axis : "0 0 1 1",
27766                 //     axisxlabels :  xdata
27767                 //     //yvalues : cols,
27768                    
27769                 // });
27770 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27771 //        
27772 //        this.load(null,xdata,{
27773 //                axis : "0 0 1 1",
27774 //                axisxlabels :  xdata
27775 //                });
27776
27777     },
27778
27779     load : function(graphtype,xdata,opts)
27780     {
27781         this.raphael.clear();
27782         if(!graphtype) {
27783             graphtype = this.graphtype;
27784         }
27785         if(!opts){
27786             opts = this.opts;
27787         }
27788         var r = this.raphael,
27789             fin = function () {
27790                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27791             },
27792             fout = function () {
27793                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27794             },
27795             pfin = function() {
27796                 this.sector.stop();
27797                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27798
27799                 if (this.label) {
27800                     this.label[0].stop();
27801                     this.label[0].attr({ r: 7.5 });
27802                     this.label[1].attr({ "font-weight": 800 });
27803                 }
27804             },
27805             pfout = function() {
27806                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27807
27808                 if (this.label) {
27809                     this.label[0].animate({ r: 5 }, 500, "bounce");
27810                     this.label[1].attr({ "font-weight": 400 });
27811                 }
27812             };
27813
27814         switch(graphtype){
27815             case 'bar':
27816                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27817                 break;
27818             case 'hbar':
27819                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27820                 break;
27821             case 'pie':
27822 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27823 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27824 //            
27825                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27826                 
27827                 break;
27828
27829         }
27830         
27831         if(this.title){
27832             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27833         }
27834         
27835     },
27836     
27837     setTitle: function(o)
27838     {
27839         this.title = o;
27840     },
27841     
27842     initEvents: function() {
27843         
27844         if(!this.href){
27845             this.el.on('click', this.onClick, this);
27846         }
27847     },
27848     
27849     onClick : function(e)
27850     {
27851         Roo.log('img onclick');
27852         this.fireEvent('click', this, e);
27853     }
27854    
27855 });
27856
27857  
27858 /*
27859  * - LGPL
27860  *
27861  * numberBox
27862  * 
27863  */
27864 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27865
27866 /**
27867  * @class Roo.bootstrap.dash.NumberBox
27868  * @extends Roo.bootstrap.Component
27869  * Bootstrap NumberBox class
27870  * @cfg {String} headline Box headline
27871  * @cfg {String} content Box content
27872  * @cfg {String} icon Box icon
27873  * @cfg {String} footer Footer text
27874  * @cfg {String} fhref Footer href
27875  * 
27876  * @constructor
27877  * Create a new NumberBox
27878  * @param {Object} config The config object
27879  */
27880
27881
27882 Roo.bootstrap.dash.NumberBox = function(config){
27883     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27884     
27885 };
27886
27887 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27888     
27889     headline : '',
27890     content : '',
27891     icon : '',
27892     footer : '',
27893     fhref : '',
27894     ficon : '',
27895     
27896     getAutoCreate : function(){
27897         
27898         var cfg = {
27899             tag : 'div',
27900             cls : 'small-box ',
27901             cn : [
27902                 {
27903                     tag : 'div',
27904                     cls : 'inner',
27905                     cn :[
27906                         {
27907                             tag : 'h3',
27908                             cls : 'roo-headline',
27909                             html : this.headline
27910                         },
27911                         {
27912                             tag : 'p',
27913                             cls : 'roo-content',
27914                             html : this.content
27915                         }
27916                     ]
27917                 }
27918             ]
27919         };
27920         
27921         if(this.icon){
27922             cfg.cn.push({
27923                 tag : 'div',
27924                 cls : 'icon',
27925                 cn :[
27926                     {
27927                         tag : 'i',
27928                         cls : 'ion ' + this.icon
27929                     }
27930                 ]
27931             });
27932         }
27933         
27934         if(this.footer){
27935             var footer = {
27936                 tag : 'a',
27937                 cls : 'small-box-footer',
27938                 href : this.fhref || '#',
27939                 html : this.footer
27940             };
27941             
27942             cfg.cn.push(footer);
27943             
27944         }
27945         
27946         return  cfg;
27947     },
27948
27949     onRender : function(ct,position){
27950         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27951
27952
27953        
27954                 
27955     },
27956
27957     setHeadline: function (value)
27958     {
27959         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27960     },
27961     
27962     setFooter: function (value, href)
27963     {
27964         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27965         
27966         if(href){
27967             this.el.select('a.small-box-footer',true).first().attr('href', href);
27968         }
27969         
27970     },
27971
27972     setContent: function (value)
27973     {
27974         this.el.select('.roo-content',true).first().dom.innerHTML = value;
27975     },
27976
27977     initEvents: function() 
27978     {   
27979         
27980     }
27981     
27982 });
27983
27984  
27985 /*
27986  * - LGPL
27987  *
27988  * TabBox
27989  * 
27990  */
27991 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27992
27993 /**
27994  * @class Roo.bootstrap.dash.TabBox
27995  * @extends Roo.bootstrap.Component
27996  * Bootstrap TabBox class
27997  * @cfg {String} title Title of the TabBox
27998  * @cfg {String} icon Icon of the TabBox
27999  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28000  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28001  * 
28002  * @constructor
28003  * Create a new TabBox
28004  * @param {Object} config The config object
28005  */
28006
28007
28008 Roo.bootstrap.dash.TabBox = function(config){
28009     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28010     this.addEvents({
28011         // raw events
28012         /**
28013          * @event addpane
28014          * When a pane is added
28015          * @param {Roo.bootstrap.dash.TabPane} pane
28016          */
28017         "addpane" : true,
28018         /**
28019          * @event activatepane
28020          * When a pane is activated
28021          * @param {Roo.bootstrap.dash.TabPane} pane
28022          */
28023         "activatepane" : true
28024         
28025          
28026     });
28027     
28028     this.panes = [];
28029 };
28030
28031 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28032
28033     title : '',
28034     icon : false,
28035     showtabs : true,
28036     tabScrollable : false,
28037     
28038     getChildContainer : function()
28039     {
28040         return this.el.select('.tab-content', true).first();
28041     },
28042     
28043     getAutoCreate : function(){
28044         
28045         var header = {
28046             tag: 'li',
28047             cls: 'pull-left header',
28048             html: this.title,
28049             cn : []
28050         };
28051         
28052         if(this.icon){
28053             header.cn.push({
28054                 tag: 'i',
28055                 cls: 'fa ' + this.icon
28056             });
28057         }
28058         
28059         var h = {
28060             tag: 'ul',
28061             cls: 'nav nav-tabs pull-right',
28062             cn: [
28063                 header
28064             ]
28065         };
28066         
28067         if(this.tabScrollable){
28068             h = {
28069                 tag: 'div',
28070                 cls: 'tab-header',
28071                 cn: [
28072                     {
28073                         tag: 'ul',
28074                         cls: 'nav nav-tabs pull-right',
28075                         cn: [
28076                             header
28077                         ]
28078                     }
28079                 ]
28080             };
28081         }
28082         
28083         var cfg = {
28084             tag: 'div',
28085             cls: 'nav-tabs-custom',
28086             cn: [
28087                 h,
28088                 {
28089                     tag: 'div',
28090                     cls: 'tab-content no-padding',
28091                     cn: []
28092                 }
28093             ]
28094         };
28095
28096         return  cfg;
28097     },
28098     initEvents : function()
28099     {
28100         //Roo.log('add add pane handler');
28101         this.on('addpane', this.onAddPane, this);
28102     },
28103      /**
28104      * Updates the box title
28105      * @param {String} html to set the title to.
28106      */
28107     setTitle : function(value)
28108     {
28109         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28110     },
28111     onAddPane : function(pane)
28112     {
28113         this.panes.push(pane);
28114         //Roo.log('addpane');
28115         //Roo.log(pane);
28116         // tabs are rendere left to right..
28117         if(!this.showtabs){
28118             return;
28119         }
28120         
28121         var ctr = this.el.select('.nav-tabs', true).first();
28122          
28123          
28124         var existing = ctr.select('.nav-tab',true);
28125         var qty = existing.getCount();;
28126         
28127         
28128         var tab = ctr.createChild({
28129             tag : 'li',
28130             cls : 'nav-tab' + (qty ? '' : ' active'),
28131             cn : [
28132                 {
28133                     tag : 'a',
28134                     href:'#',
28135                     html : pane.title
28136                 }
28137             ]
28138         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28139         pane.tab = tab;
28140         
28141         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28142         if (!qty) {
28143             pane.el.addClass('active');
28144         }
28145         
28146                 
28147     },
28148     onTabClick : function(ev,un,ob,pane)
28149     {
28150         //Roo.log('tab - prev default');
28151         ev.preventDefault();
28152         
28153         
28154         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28155         pane.tab.addClass('active');
28156         //Roo.log(pane.title);
28157         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28158         // technically we should have a deactivate event.. but maybe add later.
28159         // and it should not de-activate the selected tab...
28160         this.fireEvent('activatepane', pane);
28161         pane.el.addClass('active');
28162         pane.fireEvent('activate');
28163         
28164         
28165     },
28166     
28167     getActivePane : function()
28168     {
28169         var r = false;
28170         Roo.each(this.panes, function(p) {
28171             if(p.el.hasClass('active')){
28172                 r = p;
28173                 return false;
28174             }
28175             
28176             return;
28177         });
28178         
28179         return r;
28180     }
28181     
28182     
28183 });
28184
28185  
28186 /*
28187  * - LGPL
28188  *
28189  * Tab pane
28190  * 
28191  */
28192 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28193 /**
28194  * @class Roo.bootstrap.TabPane
28195  * @extends Roo.bootstrap.Component
28196  * Bootstrap TabPane class
28197  * @cfg {Boolean} active (false | true) Default false
28198  * @cfg {String} title title of panel
28199
28200  * 
28201  * @constructor
28202  * Create a new TabPane
28203  * @param {Object} config The config object
28204  */
28205
28206 Roo.bootstrap.dash.TabPane = function(config){
28207     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28208     
28209     this.addEvents({
28210         // raw events
28211         /**
28212          * @event activate
28213          * When a pane is activated
28214          * @param {Roo.bootstrap.dash.TabPane} pane
28215          */
28216         "activate" : true
28217          
28218     });
28219 };
28220
28221 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28222     
28223     active : false,
28224     title : '',
28225     
28226     // the tabBox that this is attached to.
28227     tab : false,
28228      
28229     getAutoCreate : function() 
28230     {
28231         var cfg = {
28232             tag: 'div',
28233             cls: 'tab-pane'
28234         };
28235         
28236         if(this.active){
28237             cfg.cls += ' active';
28238         }
28239         
28240         return cfg;
28241     },
28242     initEvents  : function()
28243     {
28244         //Roo.log('trigger add pane handler');
28245         this.parent().fireEvent('addpane', this)
28246     },
28247     
28248      /**
28249      * Updates the tab title 
28250      * @param {String} html to set the title to.
28251      */
28252     setTitle: function(str)
28253     {
28254         if (!this.tab) {
28255             return;
28256         }
28257         this.title = str;
28258         this.tab.select('a', true).first().dom.innerHTML = str;
28259         
28260     }
28261     
28262     
28263     
28264 });
28265
28266  
28267
28268
28269  /*
28270  * - LGPL
28271  *
28272  * menu
28273  * 
28274  */
28275 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28276
28277 /**
28278  * @class Roo.bootstrap.menu.Menu
28279  * @extends Roo.bootstrap.Component
28280  * Bootstrap Menu class - container for Menu
28281  * @cfg {String} html Text of the menu
28282  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28283  * @cfg {String} icon Font awesome icon
28284  * @cfg {String} pos Menu align to (top | bottom) default bottom
28285  * 
28286  * 
28287  * @constructor
28288  * Create a new Menu
28289  * @param {Object} config The config object
28290  */
28291
28292
28293 Roo.bootstrap.menu.Menu = function(config){
28294     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28295     
28296     this.addEvents({
28297         /**
28298          * @event beforeshow
28299          * Fires before this menu is displayed
28300          * @param {Roo.bootstrap.menu.Menu} this
28301          */
28302         beforeshow : true,
28303         /**
28304          * @event beforehide
28305          * Fires before this menu is hidden
28306          * @param {Roo.bootstrap.menu.Menu} this
28307          */
28308         beforehide : true,
28309         /**
28310          * @event show
28311          * Fires after this menu is displayed
28312          * @param {Roo.bootstrap.menu.Menu} this
28313          */
28314         show : true,
28315         /**
28316          * @event hide
28317          * Fires after this menu is hidden
28318          * @param {Roo.bootstrap.menu.Menu} this
28319          */
28320         hide : true,
28321         /**
28322          * @event click
28323          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28324          * @param {Roo.bootstrap.menu.Menu} this
28325          * @param {Roo.EventObject} e
28326          */
28327         click : true
28328     });
28329     
28330 };
28331
28332 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28333     
28334     submenu : false,
28335     html : '',
28336     weight : 'default',
28337     icon : false,
28338     pos : 'bottom',
28339     
28340     
28341     getChildContainer : function() {
28342         if(this.isSubMenu){
28343             return this.el;
28344         }
28345         
28346         return this.el.select('ul.dropdown-menu', true).first();  
28347     },
28348     
28349     getAutoCreate : function()
28350     {
28351         var text = [
28352             {
28353                 tag : 'span',
28354                 cls : 'roo-menu-text',
28355                 html : this.html
28356             }
28357         ];
28358         
28359         if(this.icon){
28360             text.unshift({
28361                 tag : 'i',
28362                 cls : 'fa ' + this.icon
28363             })
28364         }
28365         
28366         
28367         var cfg = {
28368             tag : 'div',
28369             cls : 'btn-group',
28370             cn : [
28371                 {
28372                     tag : 'button',
28373                     cls : 'dropdown-button btn btn-' + this.weight,
28374                     cn : text
28375                 },
28376                 {
28377                     tag : 'button',
28378                     cls : 'dropdown-toggle btn btn-' + this.weight,
28379                     cn : [
28380                         {
28381                             tag : 'span',
28382                             cls : 'caret'
28383                         }
28384                     ]
28385                 },
28386                 {
28387                     tag : 'ul',
28388                     cls : 'dropdown-menu'
28389                 }
28390             ]
28391             
28392         };
28393         
28394         if(this.pos == 'top'){
28395             cfg.cls += ' dropup';
28396         }
28397         
28398         if(this.isSubMenu){
28399             cfg = {
28400                 tag : 'ul',
28401                 cls : 'dropdown-menu'
28402             }
28403         }
28404         
28405         return cfg;
28406     },
28407     
28408     onRender : function(ct, position)
28409     {
28410         this.isSubMenu = ct.hasClass('dropdown-submenu');
28411         
28412         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28413     },
28414     
28415     initEvents : function() 
28416     {
28417         if(this.isSubMenu){
28418             return;
28419         }
28420         
28421         this.hidden = true;
28422         
28423         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28424         this.triggerEl.on('click', this.onTriggerPress, this);
28425         
28426         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28427         this.buttonEl.on('click', this.onClick, this);
28428         
28429     },
28430     
28431     list : function()
28432     {
28433         if(this.isSubMenu){
28434             return this.el;
28435         }
28436         
28437         return this.el.select('ul.dropdown-menu', true).first();
28438     },
28439     
28440     onClick : function(e)
28441     {
28442         this.fireEvent("click", this, e);
28443     },
28444     
28445     onTriggerPress  : function(e)
28446     {   
28447         if (this.isVisible()) {
28448             this.hide();
28449         } else {
28450             this.show();
28451         }
28452     },
28453     
28454     isVisible : function(){
28455         return !this.hidden;
28456     },
28457     
28458     show : function()
28459     {
28460         this.fireEvent("beforeshow", this);
28461         
28462         this.hidden = false;
28463         this.el.addClass('open');
28464         
28465         Roo.get(document).on("mouseup", this.onMouseUp, this);
28466         
28467         this.fireEvent("show", this);
28468         
28469         
28470     },
28471     
28472     hide : function()
28473     {
28474         this.fireEvent("beforehide", this);
28475         
28476         this.hidden = true;
28477         this.el.removeClass('open');
28478         
28479         Roo.get(document).un("mouseup", this.onMouseUp);
28480         
28481         this.fireEvent("hide", this);
28482     },
28483     
28484     onMouseUp : function()
28485     {
28486         this.hide();
28487     }
28488     
28489 });
28490
28491  
28492  /*
28493  * - LGPL
28494  *
28495  * menu item
28496  * 
28497  */
28498 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28499
28500 /**
28501  * @class Roo.bootstrap.menu.Item
28502  * @extends Roo.bootstrap.Component
28503  * Bootstrap MenuItem class
28504  * @cfg {Boolean} submenu (true | false) default false
28505  * @cfg {String} html text of the item
28506  * @cfg {String} href the link
28507  * @cfg {Boolean} disable (true | false) default false
28508  * @cfg {Boolean} preventDefault (true | false) default true
28509  * @cfg {String} icon Font awesome icon
28510  * @cfg {String} pos Submenu align to (left | right) default right 
28511  * 
28512  * 
28513  * @constructor
28514  * Create a new Item
28515  * @param {Object} config The config object
28516  */
28517
28518
28519 Roo.bootstrap.menu.Item = function(config){
28520     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28521     this.addEvents({
28522         /**
28523          * @event mouseover
28524          * Fires when the mouse is hovering over this menu
28525          * @param {Roo.bootstrap.menu.Item} this
28526          * @param {Roo.EventObject} e
28527          */
28528         mouseover : true,
28529         /**
28530          * @event mouseout
28531          * Fires when the mouse exits this menu
28532          * @param {Roo.bootstrap.menu.Item} this
28533          * @param {Roo.EventObject} e
28534          */
28535         mouseout : true,
28536         // raw events
28537         /**
28538          * @event click
28539          * The raw click event for the entire grid.
28540          * @param {Roo.EventObject} e
28541          */
28542         click : true
28543     });
28544 };
28545
28546 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28547     
28548     submenu : false,
28549     href : '',
28550     html : '',
28551     preventDefault: true,
28552     disable : false,
28553     icon : false,
28554     pos : 'right',
28555     
28556     getAutoCreate : function()
28557     {
28558         var text = [
28559             {
28560                 tag : 'span',
28561                 cls : 'roo-menu-item-text',
28562                 html : this.html
28563             }
28564         ];
28565         
28566         if(this.icon){
28567             text.unshift({
28568                 tag : 'i',
28569                 cls : 'fa ' + this.icon
28570             })
28571         }
28572         
28573         var cfg = {
28574             tag : 'li',
28575             cn : [
28576                 {
28577                     tag : 'a',
28578                     href : this.href || '#',
28579                     cn : text
28580                 }
28581             ]
28582         };
28583         
28584         if(this.disable){
28585             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28586         }
28587         
28588         if(this.submenu){
28589             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28590             
28591             if(this.pos == 'left'){
28592                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28593             }
28594         }
28595         
28596         return cfg;
28597     },
28598     
28599     initEvents : function() 
28600     {
28601         this.el.on('mouseover', this.onMouseOver, this);
28602         this.el.on('mouseout', this.onMouseOut, this);
28603         
28604         this.el.select('a', true).first().on('click', this.onClick, this);
28605         
28606     },
28607     
28608     onClick : function(e)
28609     {
28610         if(this.preventDefault){
28611             e.preventDefault();
28612         }
28613         
28614         this.fireEvent("click", this, e);
28615     },
28616     
28617     onMouseOver : function(e)
28618     {
28619         if(this.submenu && this.pos == 'left'){
28620             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28621         }
28622         
28623         this.fireEvent("mouseover", this, e);
28624     },
28625     
28626     onMouseOut : function(e)
28627     {
28628         this.fireEvent("mouseout", this, e);
28629     }
28630 });
28631
28632  
28633
28634  /*
28635  * - LGPL
28636  *
28637  * menu separator
28638  * 
28639  */
28640 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28641
28642 /**
28643  * @class Roo.bootstrap.menu.Separator
28644  * @extends Roo.bootstrap.Component
28645  * Bootstrap Separator class
28646  * 
28647  * @constructor
28648  * Create a new Separator
28649  * @param {Object} config The config object
28650  */
28651
28652
28653 Roo.bootstrap.menu.Separator = function(config){
28654     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28655 };
28656
28657 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28658     
28659     getAutoCreate : function(){
28660         var cfg = {
28661             tag : 'li',
28662             cls: 'divider'
28663         };
28664         
28665         return cfg;
28666     }
28667    
28668 });
28669
28670  
28671
28672  /*
28673  * - LGPL
28674  *
28675  * Tooltip
28676  * 
28677  */
28678
28679 /**
28680  * @class Roo.bootstrap.Tooltip
28681  * Bootstrap Tooltip class
28682  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28683  * to determine which dom element triggers the tooltip.
28684  * 
28685  * It needs to add support for additional attributes like tooltip-position
28686  * 
28687  * @constructor
28688  * Create a new Toolti
28689  * @param {Object} config The config object
28690  */
28691
28692 Roo.bootstrap.Tooltip = function(config){
28693     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28694     
28695     this.alignment = Roo.bootstrap.Tooltip.alignment;
28696     
28697     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28698         this.alignment = config.alignment;
28699     }
28700     
28701 };
28702
28703 Roo.apply(Roo.bootstrap.Tooltip, {
28704     /**
28705      * @function init initialize tooltip monitoring.
28706      * @static
28707      */
28708     currentEl : false,
28709     currentTip : false,
28710     currentRegion : false,
28711     
28712     //  init : delay?
28713     
28714     init : function()
28715     {
28716         Roo.get(document).on('mouseover', this.enter ,this);
28717         Roo.get(document).on('mouseout', this.leave, this);
28718          
28719         
28720         this.currentTip = new Roo.bootstrap.Tooltip();
28721     },
28722     
28723     enter : function(ev)
28724     {
28725         var dom = ev.getTarget();
28726         
28727         //Roo.log(['enter',dom]);
28728         var el = Roo.fly(dom);
28729         if (this.currentEl) {
28730             //Roo.log(dom);
28731             //Roo.log(this.currentEl);
28732             //Roo.log(this.currentEl.contains(dom));
28733             if (this.currentEl == el) {
28734                 return;
28735             }
28736             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28737                 return;
28738             }
28739
28740         }
28741         
28742         if (this.currentTip.el) {
28743             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28744         }    
28745         //Roo.log(ev);
28746         
28747         if(!el || el.dom == document){
28748             return;
28749         }
28750         
28751         var bindEl = el;
28752         
28753         // you can not look for children, as if el is the body.. then everythign is the child..
28754         if (!el.attr('tooltip')) { //
28755             if (!el.select("[tooltip]").elements.length) {
28756                 return;
28757             }
28758             // is the mouse over this child...?
28759             bindEl = el.select("[tooltip]").first();
28760             var xy = ev.getXY();
28761             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28762                 //Roo.log("not in region.");
28763                 return;
28764             }
28765             //Roo.log("child element over..");
28766             
28767         }
28768         this.currentEl = bindEl;
28769         this.currentTip.bind(bindEl);
28770         this.currentRegion = Roo.lib.Region.getRegion(dom);
28771         this.currentTip.enter();
28772         
28773     },
28774     leave : function(ev)
28775     {
28776         var dom = ev.getTarget();
28777         //Roo.log(['leave',dom]);
28778         if (!this.currentEl) {
28779             return;
28780         }
28781         
28782         
28783         if (dom != this.currentEl.dom) {
28784             return;
28785         }
28786         var xy = ev.getXY();
28787         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28788             return;
28789         }
28790         // only activate leave if mouse cursor is outside... bounding box..
28791         
28792         
28793         
28794         
28795         if (this.currentTip) {
28796             this.currentTip.leave();
28797         }
28798         //Roo.log('clear currentEl');
28799         this.currentEl = false;
28800         
28801         
28802     },
28803     alignment : {
28804         'left' : ['r-l', [-2,0], 'right'],
28805         'right' : ['l-r', [2,0], 'left'],
28806         'bottom' : ['t-b', [0,2], 'top'],
28807         'top' : [ 'b-t', [0,-2], 'bottom']
28808     }
28809     
28810 });
28811
28812
28813 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28814     
28815     
28816     bindEl : false,
28817     
28818     delay : null, // can be { show : 300 , hide: 500}
28819     
28820     timeout : null,
28821     
28822     hoverState : null, //???
28823     
28824     placement : 'bottom', 
28825     
28826     alignment : false,
28827     
28828     getAutoCreate : function(){
28829     
28830         var cfg = {
28831            cls : 'tooltip',   
28832            role : 'tooltip',
28833            cn : [
28834                 {
28835                     cls : 'tooltip-arrow arrow'
28836                 },
28837                 {
28838                     cls : 'tooltip-inner'
28839                 }
28840            ]
28841         };
28842         
28843         return cfg;
28844     },
28845     bind : function(el)
28846     {
28847         this.bindEl = el;
28848     },
28849     
28850     initEvents : function()
28851     {
28852         this.arrowEl = this.el.select('.arrow', true).first();
28853         this.innerEl = this.el.select('.tooltip-inner', true).first();
28854     },
28855     
28856     enter : function () {
28857        
28858         if (this.timeout != null) {
28859             clearTimeout(this.timeout);
28860         }
28861         
28862         this.hoverState = 'in';
28863          //Roo.log("enter - show");
28864         if (!this.delay || !this.delay.show) {
28865             this.show();
28866             return;
28867         }
28868         var _t = this;
28869         this.timeout = setTimeout(function () {
28870             if (_t.hoverState == 'in') {
28871                 _t.show();
28872             }
28873         }, this.delay.show);
28874     },
28875     leave : function()
28876     {
28877         clearTimeout(this.timeout);
28878     
28879         this.hoverState = 'out';
28880          if (!this.delay || !this.delay.hide) {
28881             this.hide();
28882             return;
28883         }
28884        
28885         var _t = this;
28886         this.timeout = setTimeout(function () {
28887             //Roo.log("leave - timeout");
28888             
28889             if (_t.hoverState == 'out') {
28890                 _t.hide();
28891                 Roo.bootstrap.Tooltip.currentEl = false;
28892             }
28893         }, delay);
28894     },
28895     
28896     show : function (msg)
28897     {
28898         if (!this.el) {
28899             this.render(document.body);
28900         }
28901         // set content.
28902         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28903         
28904         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28905         
28906         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28907         
28908         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28909                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28910         
28911         var placement = typeof this.placement == 'function' ?
28912             this.placement.call(this, this.el, on_el) :
28913             this.placement;
28914             
28915         var autoToken = /\s?auto?\s?/i;
28916         var autoPlace = autoToken.test(placement);
28917         if (autoPlace) {
28918             placement = placement.replace(autoToken, '') || 'top';
28919         }
28920         
28921         //this.el.detach()
28922         //this.el.setXY([0,0]);
28923         this.el.show();
28924         //this.el.dom.style.display='block';
28925         
28926         //this.el.appendTo(on_el);
28927         
28928         var p = this.getPosition();
28929         var box = this.el.getBox();
28930         
28931         if (autoPlace) {
28932             // fixme..
28933         }
28934         
28935         var align = this.alignment[placement];
28936         
28937         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28938         
28939         if(placement == 'top' || placement == 'bottom'){
28940             if(xy[0] < 0){
28941                 placement = 'right';
28942             }
28943             
28944             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28945                 placement = 'left';
28946             }
28947             
28948             var scroll = Roo.select('body', true).first().getScroll();
28949             
28950             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28951                 placement = 'top';
28952             }
28953             
28954             align = this.alignment[placement];
28955             
28956             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28957             
28958         }
28959         
28960         this.el.alignTo(this.bindEl, align[0],align[1]);
28961         //var arrow = this.el.select('.arrow',true).first();
28962         //arrow.set(align[2], 
28963         
28964         this.el.addClass(placement);
28965         this.el.addClass("bs-tooltip-"+ placement);
28966         
28967         this.el.addClass('in fade show');
28968         
28969         this.hoverState = null;
28970         
28971         if (this.el.hasClass('fade')) {
28972             // fade it?
28973         }
28974         
28975         
28976         
28977         
28978         
28979     },
28980     hide : function()
28981     {
28982          
28983         if (!this.el) {
28984             return;
28985         }
28986         //this.el.setXY([0,0]);
28987         this.el.removeClass(['show', 'in']);
28988         //this.el.hide();
28989         
28990     }
28991     
28992 });
28993  
28994
28995  /*
28996  * - LGPL
28997  *
28998  * Location Picker
28999  * 
29000  */
29001
29002 /**
29003  * @class Roo.bootstrap.LocationPicker
29004  * @extends Roo.bootstrap.Component
29005  * Bootstrap LocationPicker class
29006  * @cfg {Number} latitude Position when init default 0
29007  * @cfg {Number} longitude Position when init default 0
29008  * @cfg {Number} zoom default 15
29009  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29010  * @cfg {Boolean} mapTypeControl default false
29011  * @cfg {Boolean} disableDoubleClickZoom default false
29012  * @cfg {Boolean} scrollwheel default true
29013  * @cfg {Boolean} streetViewControl default false
29014  * @cfg {Number} radius default 0
29015  * @cfg {String} locationName
29016  * @cfg {Boolean} draggable default true
29017  * @cfg {Boolean} enableAutocomplete default false
29018  * @cfg {Boolean} enableReverseGeocode default true
29019  * @cfg {String} markerTitle
29020  * 
29021  * @constructor
29022  * Create a new LocationPicker
29023  * @param {Object} config The config object
29024  */
29025
29026
29027 Roo.bootstrap.LocationPicker = function(config){
29028     
29029     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29030     
29031     this.addEvents({
29032         /**
29033          * @event initial
29034          * Fires when the picker initialized.
29035          * @param {Roo.bootstrap.LocationPicker} this
29036          * @param {Google Location} location
29037          */
29038         initial : true,
29039         /**
29040          * @event positionchanged
29041          * Fires when the picker position changed.
29042          * @param {Roo.bootstrap.LocationPicker} this
29043          * @param {Google Location} location
29044          */
29045         positionchanged : true,
29046         /**
29047          * @event resize
29048          * Fires when the map resize.
29049          * @param {Roo.bootstrap.LocationPicker} this
29050          */
29051         resize : true,
29052         /**
29053          * @event show
29054          * Fires when the map show.
29055          * @param {Roo.bootstrap.LocationPicker} this
29056          */
29057         show : true,
29058         /**
29059          * @event hide
29060          * Fires when the map hide.
29061          * @param {Roo.bootstrap.LocationPicker} this
29062          */
29063         hide : true,
29064         /**
29065          * @event mapClick
29066          * Fires when click the map.
29067          * @param {Roo.bootstrap.LocationPicker} this
29068          * @param {Map event} e
29069          */
29070         mapClick : true,
29071         /**
29072          * @event mapRightClick
29073          * Fires when right click the map.
29074          * @param {Roo.bootstrap.LocationPicker} this
29075          * @param {Map event} e
29076          */
29077         mapRightClick : true,
29078         /**
29079          * @event markerClick
29080          * Fires when click the marker.
29081          * @param {Roo.bootstrap.LocationPicker} this
29082          * @param {Map event} e
29083          */
29084         markerClick : true,
29085         /**
29086          * @event markerRightClick
29087          * Fires when right click the marker.
29088          * @param {Roo.bootstrap.LocationPicker} this
29089          * @param {Map event} e
29090          */
29091         markerRightClick : true,
29092         /**
29093          * @event OverlayViewDraw
29094          * Fires when OverlayView Draw
29095          * @param {Roo.bootstrap.LocationPicker} this
29096          */
29097         OverlayViewDraw : true,
29098         /**
29099          * @event OverlayViewOnAdd
29100          * Fires when OverlayView Draw
29101          * @param {Roo.bootstrap.LocationPicker} this
29102          */
29103         OverlayViewOnAdd : true,
29104         /**
29105          * @event OverlayViewOnRemove
29106          * Fires when OverlayView Draw
29107          * @param {Roo.bootstrap.LocationPicker} this
29108          */
29109         OverlayViewOnRemove : true,
29110         /**
29111          * @event OverlayViewShow
29112          * Fires when OverlayView Draw
29113          * @param {Roo.bootstrap.LocationPicker} this
29114          * @param {Pixel} cpx
29115          */
29116         OverlayViewShow : true,
29117         /**
29118          * @event OverlayViewHide
29119          * Fires when OverlayView Draw
29120          * @param {Roo.bootstrap.LocationPicker} this
29121          */
29122         OverlayViewHide : true,
29123         /**
29124          * @event loadexception
29125          * Fires when load google lib failed.
29126          * @param {Roo.bootstrap.LocationPicker} this
29127          */
29128         loadexception : true
29129     });
29130         
29131 };
29132
29133 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29134     
29135     gMapContext: false,
29136     
29137     latitude: 0,
29138     longitude: 0,
29139     zoom: 15,
29140     mapTypeId: false,
29141     mapTypeControl: false,
29142     disableDoubleClickZoom: false,
29143     scrollwheel: true,
29144     streetViewControl: false,
29145     radius: 0,
29146     locationName: '',
29147     draggable: true,
29148     enableAutocomplete: false,
29149     enableReverseGeocode: true,
29150     markerTitle: '',
29151     
29152     getAutoCreate: function()
29153     {
29154
29155         var cfg = {
29156             tag: 'div',
29157             cls: 'roo-location-picker'
29158         };
29159         
29160         return cfg
29161     },
29162     
29163     initEvents: function(ct, position)
29164     {       
29165         if(!this.el.getWidth() || this.isApplied()){
29166             return;
29167         }
29168         
29169         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29170         
29171         this.initial();
29172     },
29173     
29174     initial: function()
29175     {
29176         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29177             this.fireEvent('loadexception', this);
29178             return;
29179         }
29180         
29181         if(!this.mapTypeId){
29182             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29183         }
29184         
29185         this.gMapContext = this.GMapContext();
29186         
29187         this.initOverlayView();
29188         
29189         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29190         
29191         var _this = this;
29192                 
29193         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29194             _this.setPosition(_this.gMapContext.marker.position);
29195         });
29196         
29197         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29198             _this.fireEvent('mapClick', this, event);
29199             
29200         });
29201
29202         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29203             _this.fireEvent('mapRightClick', this, event);
29204             
29205         });
29206         
29207         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29208             _this.fireEvent('markerClick', this, event);
29209             
29210         });
29211
29212         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29213             _this.fireEvent('markerRightClick', this, event);
29214             
29215         });
29216         
29217         this.setPosition(this.gMapContext.location);
29218         
29219         this.fireEvent('initial', this, this.gMapContext.location);
29220     },
29221     
29222     initOverlayView: function()
29223     {
29224         var _this = this;
29225         
29226         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29227             
29228             draw: function()
29229             {
29230                 _this.fireEvent('OverlayViewDraw', _this);
29231             },
29232             
29233             onAdd: function()
29234             {
29235                 _this.fireEvent('OverlayViewOnAdd', _this);
29236             },
29237             
29238             onRemove: function()
29239             {
29240                 _this.fireEvent('OverlayViewOnRemove', _this);
29241             },
29242             
29243             show: function(cpx)
29244             {
29245                 _this.fireEvent('OverlayViewShow', _this, cpx);
29246             },
29247             
29248             hide: function()
29249             {
29250                 _this.fireEvent('OverlayViewHide', _this);
29251             }
29252             
29253         });
29254     },
29255     
29256     fromLatLngToContainerPixel: function(event)
29257     {
29258         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29259     },
29260     
29261     isApplied: function() 
29262     {
29263         return this.getGmapContext() == false ? false : true;
29264     },
29265     
29266     getGmapContext: function() 
29267     {
29268         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29269     },
29270     
29271     GMapContext: function() 
29272     {
29273         var position = new google.maps.LatLng(this.latitude, this.longitude);
29274         
29275         var _map = new google.maps.Map(this.el.dom, {
29276             center: position,
29277             zoom: this.zoom,
29278             mapTypeId: this.mapTypeId,
29279             mapTypeControl: this.mapTypeControl,
29280             disableDoubleClickZoom: this.disableDoubleClickZoom,
29281             scrollwheel: this.scrollwheel,
29282             streetViewControl: this.streetViewControl,
29283             locationName: this.locationName,
29284             draggable: this.draggable,
29285             enableAutocomplete: this.enableAutocomplete,
29286             enableReverseGeocode: this.enableReverseGeocode
29287         });
29288         
29289         var _marker = new google.maps.Marker({
29290             position: position,
29291             map: _map,
29292             title: this.markerTitle,
29293             draggable: this.draggable
29294         });
29295         
29296         return {
29297             map: _map,
29298             marker: _marker,
29299             circle: null,
29300             location: position,
29301             radius: this.radius,
29302             locationName: this.locationName,
29303             addressComponents: {
29304                 formatted_address: null,
29305                 addressLine1: null,
29306                 addressLine2: null,
29307                 streetName: null,
29308                 streetNumber: null,
29309                 city: null,
29310                 district: null,
29311                 state: null,
29312                 stateOrProvince: null
29313             },
29314             settings: this,
29315             domContainer: this.el.dom,
29316             geodecoder: new google.maps.Geocoder()
29317         };
29318     },
29319     
29320     drawCircle: function(center, radius, options) 
29321     {
29322         if (this.gMapContext.circle != null) {
29323             this.gMapContext.circle.setMap(null);
29324         }
29325         if (radius > 0) {
29326             radius *= 1;
29327             options = Roo.apply({}, options, {
29328                 strokeColor: "#0000FF",
29329                 strokeOpacity: .35,
29330                 strokeWeight: 2,
29331                 fillColor: "#0000FF",
29332                 fillOpacity: .2
29333             });
29334             
29335             options.map = this.gMapContext.map;
29336             options.radius = radius;
29337             options.center = center;
29338             this.gMapContext.circle = new google.maps.Circle(options);
29339             return this.gMapContext.circle;
29340         }
29341         
29342         return null;
29343     },
29344     
29345     setPosition: function(location) 
29346     {
29347         this.gMapContext.location = location;
29348         this.gMapContext.marker.setPosition(location);
29349         this.gMapContext.map.panTo(location);
29350         this.drawCircle(location, this.gMapContext.radius, {});
29351         
29352         var _this = this;
29353         
29354         if (this.gMapContext.settings.enableReverseGeocode) {
29355             this.gMapContext.geodecoder.geocode({
29356                 latLng: this.gMapContext.location
29357             }, function(results, status) {
29358                 
29359                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29360                     _this.gMapContext.locationName = results[0].formatted_address;
29361                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29362                     
29363                     _this.fireEvent('positionchanged', this, location);
29364                 }
29365             });
29366             
29367             return;
29368         }
29369         
29370         this.fireEvent('positionchanged', this, location);
29371     },
29372     
29373     resize: function()
29374     {
29375         google.maps.event.trigger(this.gMapContext.map, "resize");
29376         
29377         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29378         
29379         this.fireEvent('resize', this);
29380     },
29381     
29382     setPositionByLatLng: function(latitude, longitude)
29383     {
29384         this.setPosition(new google.maps.LatLng(latitude, longitude));
29385     },
29386     
29387     getCurrentPosition: function() 
29388     {
29389         return {
29390             latitude: this.gMapContext.location.lat(),
29391             longitude: this.gMapContext.location.lng()
29392         };
29393     },
29394     
29395     getAddressName: function() 
29396     {
29397         return this.gMapContext.locationName;
29398     },
29399     
29400     getAddressComponents: function() 
29401     {
29402         return this.gMapContext.addressComponents;
29403     },
29404     
29405     address_component_from_google_geocode: function(address_components) 
29406     {
29407         var result = {};
29408         
29409         for (var i = 0; i < address_components.length; i++) {
29410             var component = address_components[i];
29411             if (component.types.indexOf("postal_code") >= 0) {
29412                 result.postalCode = component.short_name;
29413             } else if (component.types.indexOf("street_number") >= 0) {
29414                 result.streetNumber = component.short_name;
29415             } else if (component.types.indexOf("route") >= 0) {
29416                 result.streetName = component.short_name;
29417             } else if (component.types.indexOf("neighborhood") >= 0) {
29418                 result.city = component.short_name;
29419             } else if (component.types.indexOf("locality") >= 0) {
29420                 result.city = component.short_name;
29421             } else if (component.types.indexOf("sublocality") >= 0) {
29422                 result.district = component.short_name;
29423             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29424                 result.stateOrProvince = component.short_name;
29425             } else if (component.types.indexOf("country") >= 0) {
29426                 result.country = component.short_name;
29427             }
29428         }
29429         
29430         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29431         result.addressLine2 = "";
29432         return result;
29433     },
29434     
29435     setZoomLevel: function(zoom)
29436     {
29437         this.gMapContext.map.setZoom(zoom);
29438     },
29439     
29440     show: function()
29441     {
29442         if(!this.el){
29443             return;
29444         }
29445         
29446         this.el.show();
29447         
29448         this.resize();
29449         
29450         this.fireEvent('show', this);
29451     },
29452     
29453     hide: function()
29454     {
29455         if(!this.el){
29456             return;
29457         }
29458         
29459         this.el.hide();
29460         
29461         this.fireEvent('hide', this);
29462     }
29463     
29464 });
29465
29466 Roo.apply(Roo.bootstrap.LocationPicker, {
29467     
29468     OverlayView : function(map, options)
29469     {
29470         options = options || {};
29471         
29472         this.setMap(map);
29473     }
29474     
29475     
29476 });/**
29477  * @class Roo.bootstrap.Alert
29478  * @extends Roo.bootstrap.Component
29479  * Bootstrap Alert class - shows an alert area box
29480  * eg
29481  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29482   Enter a valid email address
29483 </div>
29484  * @licence LGPL
29485  * @cfg {String} title The title of alert
29486  * @cfg {String} html The content of alert
29487  * @cfg {String} weight (  success | info | warning | danger )
29488  * @cfg {String} faicon font-awesomeicon
29489  * 
29490  * @constructor
29491  * Create a new alert
29492  * @param {Object} config The config object
29493  */
29494
29495
29496 Roo.bootstrap.Alert = function(config){
29497     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29498     
29499 };
29500
29501 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29502     
29503     title: '',
29504     html: '',
29505     weight: false,
29506     faicon: false,
29507     
29508     getAutoCreate : function()
29509     {
29510         
29511         var cfg = {
29512             tag : 'div',
29513             cls : 'alert',
29514             cn : [
29515                 {
29516                     tag : 'i',
29517                     cls : 'roo-alert-icon'
29518                     
29519                 },
29520                 {
29521                     tag : 'b',
29522                     cls : 'roo-alert-title',
29523                     html : this.title
29524                 },
29525                 {
29526                     tag : 'span',
29527                     cls : 'roo-alert-text',
29528                     html : this.html
29529                 }
29530             ]
29531         };
29532         
29533         if(this.faicon){
29534             cfg.cn[0].cls += ' fa ' + this.faicon;
29535         }
29536         
29537         if(this.weight){
29538             cfg.cls += ' alert-' + this.weight;
29539         }
29540         
29541         return cfg;
29542     },
29543     
29544     initEvents: function() 
29545     {
29546         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29547     },
29548     
29549     setTitle : function(str)
29550     {
29551         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29552     },
29553     
29554     setText : function(str)
29555     {
29556         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29557     },
29558     
29559     setWeight : function(weight)
29560     {
29561         if(this.weight){
29562             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29563         }
29564         
29565         this.weight = weight;
29566         
29567         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29568     },
29569     
29570     setIcon : function(icon)
29571     {
29572         if(this.faicon){
29573             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29574         }
29575         
29576         this.faicon = icon;
29577         
29578         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29579     },
29580     
29581     hide: function() 
29582     {
29583         this.el.hide();   
29584     },
29585     
29586     show: function() 
29587     {  
29588         this.el.show();   
29589     }
29590     
29591 });
29592
29593  
29594 /*
29595 * Licence: LGPL
29596 */
29597
29598 /**
29599  * @class Roo.bootstrap.UploadCropbox
29600  * @extends Roo.bootstrap.Component
29601  * Bootstrap UploadCropbox class
29602  * @cfg {String} emptyText show when image has been loaded
29603  * @cfg {String} rotateNotify show when image too small to rotate
29604  * @cfg {Number} errorTimeout default 3000
29605  * @cfg {Number} minWidth default 300
29606  * @cfg {Number} minHeight default 300
29607  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29608  * @cfg {Boolean} isDocument (true|false) default false
29609  * @cfg {String} url action url
29610  * @cfg {String} paramName default 'imageUpload'
29611  * @cfg {String} method default POST
29612  * @cfg {Boolean} loadMask (true|false) default true
29613  * @cfg {Boolean} loadingText default 'Loading...'
29614  * 
29615  * @constructor
29616  * Create a new UploadCropbox
29617  * @param {Object} config The config object
29618  */
29619
29620 Roo.bootstrap.UploadCropbox = function(config){
29621     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29622     
29623     this.addEvents({
29624         /**
29625          * @event beforeselectfile
29626          * Fire before select file
29627          * @param {Roo.bootstrap.UploadCropbox} this
29628          */
29629         "beforeselectfile" : true,
29630         /**
29631          * @event initial
29632          * Fire after initEvent
29633          * @param {Roo.bootstrap.UploadCropbox} this
29634          */
29635         "initial" : true,
29636         /**
29637          * @event crop
29638          * Fire after initEvent
29639          * @param {Roo.bootstrap.UploadCropbox} this
29640          * @param {String} data
29641          */
29642         "crop" : true,
29643         /**
29644          * @event prepare
29645          * Fire when preparing the file data
29646          * @param {Roo.bootstrap.UploadCropbox} this
29647          * @param {Object} file
29648          */
29649         "prepare" : true,
29650         /**
29651          * @event exception
29652          * Fire when get exception
29653          * @param {Roo.bootstrap.UploadCropbox} this
29654          * @param {XMLHttpRequest} xhr
29655          */
29656         "exception" : true,
29657         /**
29658          * @event beforeloadcanvas
29659          * Fire before load the canvas
29660          * @param {Roo.bootstrap.UploadCropbox} this
29661          * @param {String} src
29662          */
29663         "beforeloadcanvas" : true,
29664         /**
29665          * @event trash
29666          * Fire when trash image
29667          * @param {Roo.bootstrap.UploadCropbox} this
29668          */
29669         "trash" : true,
29670         /**
29671          * @event download
29672          * Fire when download the image
29673          * @param {Roo.bootstrap.UploadCropbox} this
29674          */
29675         "download" : true,
29676         /**
29677          * @event footerbuttonclick
29678          * Fire when footerbuttonclick
29679          * @param {Roo.bootstrap.UploadCropbox} this
29680          * @param {String} type
29681          */
29682         "footerbuttonclick" : true,
29683         /**
29684          * @event resize
29685          * Fire when resize
29686          * @param {Roo.bootstrap.UploadCropbox} this
29687          */
29688         "resize" : true,
29689         /**
29690          * @event rotate
29691          * Fire when rotate the image
29692          * @param {Roo.bootstrap.UploadCropbox} this
29693          * @param {String} pos
29694          */
29695         "rotate" : true,
29696         /**
29697          * @event inspect
29698          * Fire when inspect the file
29699          * @param {Roo.bootstrap.UploadCropbox} this
29700          * @param {Object} file
29701          */
29702         "inspect" : true,
29703         /**
29704          * @event upload
29705          * Fire when xhr upload the file
29706          * @param {Roo.bootstrap.UploadCropbox} this
29707          * @param {Object} data
29708          */
29709         "upload" : true,
29710         /**
29711          * @event arrange
29712          * Fire when arrange the file data
29713          * @param {Roo.bootstrap.UploadCropbox} this
29714          * @param {Object} formData
29715          */
29716         "arrange" : true
29717     });
29718     
29719     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29720 };
29721
29722 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29723     
29724     emptyText : 'Click to upload image',
29725     rotateNotify : 'Image is too small to rotate',
29726     errorTimeout : 3000,
29727     scale : 0,
29728     baseScale : 1,
29729     rotate : 0,
29730     dragable : false,
29731     pinching : false,
29732     mouseX : 0,
29733     mouseY : 0,
29734     cropData : false,
29735     minWidth : 300,
29736     minHeight : 300,
29737     file : false,
29738     exif : {},
29739     baseRotate : 1,
29740     cropType : 'image/jpeg',
29741     buttons : false,
29742     canvasLoaded : false,
29743     isDocument : false,
29744     method : 'POST',
29745     paramName : 'imageUpload',
29746     loadMask : true,
29747     loadingText : 'Loading...',
29748     maskEl : false,
29749     
29750     getAutoCreate : function()
29751     {
29752         var cfg = {
29753             tag : 'div',
29754             cls : 'roo-upload-cropbox',
29755             cn : [
29756                 {
29757                     tag : 'input',
29758                     cls : 'roo-upload-cropbox-selector',
29759                     type : 'file'
29760                 },
29761                 {
29762                     tag : 'div',
29763                     cls : 'roo-upload-cropbox-body',
29764                     style : 'cursor:pointer',
29765                     cn : [
29766                         {
29767                             tag : 'div',
29768                             cls : 'roo-upload-cropbox-preview'
29769                         },
29770                         {
29771                             tag : 'div',
29772                             cls : 'roo-upload-cropbox-thumb'
29773                         },
29774                         {
29775                             tag : 'div',
29776                             cls : 'roo-upload-cropbox-empty-notify',
29777                             html : this.emptyText
29778                         },
29779                         {
29780                             tag : 'div',
29781                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29782                             html : this.rotateNotify
29783                         }
29784                     ]
29785                 },
29786                 {
29787                     tag : 'div',
29788                     cls : 'roo-upload-cropbox-footer',
29789                     cn : {
29790                         tag : 'div',
29791                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29792                         cn : []
29793                     }
29794                 }
29795             ]
29796         };
29797         
29798         return cfg;
29799     },
29800     
29801     onRender : function(ct, position)
29802     {
29803         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29804         
29805         if (this.buttons.length) {
29806             
29807             Roo.each(this.buttons, function(bb) {
29808                 
29809                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29810                 
29811                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29812                 
29813             }, this);
29814         }
29815         
29816         if(this.loadMask){
29817             this.maskEl = this.el;
29818         }
29819     },
29820     
29821     initEvents : function()
29822     {
29823         this.urlAPI = (window.createObjectURL && window) || 
29824                                 (window.URL && URL.revokeObjectURL && URL) || 
29825                                 (window.webkitURL && webkitURL);
29826                         
29827         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29828         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29829         
29830         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29831         this.selectorEl.hide();
29832         
29833         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29834         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29835         
29836         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29837         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29838         this.thumbEl.hide();
29839         
29840         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29841         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29842         
29843         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29844         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29845         this.errorEl.hide();
29846         
29847         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29848         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29849         this.footerEl.hide();
29850         
29851         this.setThumbBoxSize();
29852         
29853         this.bind();
29854         
29855         this.resize();
29856         
29857         this.fireEvent('initial', this);
29858     },
29859
29860     bind : function()
29861     {
29862         var _this = this;
29863         
29864         window.addEventListener("resize", function() { _this.resize(); } );
29865         
29866         this.bodyEl.on('click', this.beforeSelectFile, this);
29867         
29868         if(Roo.isTouch){
29869             this.bodyEl.on('touchstart', this.onTouchStart, this);
29870             this.bodyEl.on('touchmove', this.onTouchMove, this);
29871             this.bodyEl.on('touchend', this.onTouchEnd, this);
29872         }
29873         
29874         if(!Roo.isTouch){
29875             this.bodyEl.on('mousedown', this.onMouseDown, this);
29876             this.bodyEl.on('mousemove', this.onMouseMove, this);
29877             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29878             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29879             Roo.get(document).on('mouseup', this.onMouseUp, this);
29880         }
29881         
29882         this.selectorEl.on('change', this.onFileSelected, this);
29883     },
29884     
29885     reset : function()
29886     {    
29887         this.scale = 0;
29888         this.baseScale = 1;
29889         this.rotate = 0;
29890         this.baseRotate = 1;
29891         this.dragable = false;
29892         this.pinching = false;
29893         this.mouseX = 0;
29894         this.mouseY = 0;
29895         this.cropData = false;
29896         this.notifyEl.dom.innerHTML = this.emptyText;
29897         
29898         this.selectorEl.dom.value = '';
29899         
29900     },
29901     
29902     resize : function()
29903     {
29904         if(this.fireEvent('resize', this) != false){
29905             this.setThumbBoxPosition();
29906             this.setCanvasPosition();
29907         }
29908     },
29909     
29910     onFooterButtonClick : function(e, el, o, type)
29911     {
29912         switch (type) {
29913             case 'rotate-left' :
29914                 this.onRotateLeft(e);
29915                 break;
29916             case 'rotate-right' :
29917                 this.onRotateRight(e);
29918                 break;
29919             case 'picture' :
29920                 this.beforeSelectFile(e);
29921                 break;
29922             case 'trash' :
29923                 this.trash(e);
29924                 break;
29925             case 'crop' :
29926                 this.crop(e);
29927                 break;
29928             case 'download' :
29929                 this.download(e);
29930                 break;
29931             default :
29932                 break;
29933         }
29934         
29935         this.fireEvent('footerbuttonclick', this, type);
29936     },
29937     
29938     beforeSelectFile : function(e)
29939     {
29940         e.preventDefault();
29941         
29942         if(this.fireEvent('beforeselectfile', this) != false){
29943             this.selectorEl.dom.click();
29944         }
29945     },
29946     
29947     onFileSelected : function(e)
29948     {
29949         e.preventDefault();
29950         
29951         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29952             return;
29953         }
29954         
29955         var file = this.selectorEl.dom.files[0];
29956         
29957         if(this.fireEvent('inspect', this, file) != false){
29958             this.prepare(file);
29959         }
29960         
29961     },
29962     
29963     trash : function(e)
29964     {
29965         this.fireEvent('trash', this);
29966     },
29967     
29968     download : function(e)
29969     {
29970         this.fireEvent('download', this);
29971     },
29972     
29973     loadCanvas : function(src)
29974     {   
29975         if(this.fireEvent('beforeloadcanvas', this, src) != false){
29976             
29977             this.reset();
29978             
29979             this.imageEl = document.createElement('img');
29980             
29981             var _this = this;
29982             
29983             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29984             
29985             this.imageEl.src = src;
29986         }
29987     },
29988     
29989     onLoadCanvas : function()
29990     {   
29991         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29992         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29993         
29994         this.bodyEl.un('click', this.beforeSelectFile, this);
29995         
29996         this.notifyEl.hide();
29997         this.thumbEl.show();
29998         this.footerEl.show();
29999         
30000         this.baseRotateLevel();
30001         
30002         if(this.isDocument){
30003             this.setThumbBoxSize();
30004         }
30005         
30006         this.setThumbBoxPosition();
30007         
30008         this.baseScaleLevel();
30009         
30010         this.draw();
30011         
30012         this.resize();
30013         
30014         this.canvasLoaded = true;
30015         
30016         if(this.loadMask){
30017             this.maskEl.unmask();
30018         }
30019         
30020     },
30021     
30022     setCanvasPosition : function()
30023     {   
30024         if(!this.canvasEl){
30025             return;
30026         }
30027         
30028         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30029         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30030         
30031         this.previewEl.setLeft(pw);
30032         this.previewEl.setTop(ph);
30033         
30034     },
30035     
30036     onMouseDown : function(e)
30037     {   
30038         e.stopEvent();
30039         
30040         this.dragable = true;
30041         this.pinching = false;
30042         
30043         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30044             this.dragable = false;
30045             return;
30046         }
30047         
30048         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30049         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30050         
30051     },
30052     
30053     onMouseMove : function(e)
30054     {   
30055         e.stopEvent();
30056         
30057         if(!this.canvasLoaded){
30058             return;
30059         }
30060         
30061         if (!this.dragable){
30062             return;
30063         }
30064         
30065         var minX = Math.ceil(this.thumbEl.getLeft(true));
30066         var minY = Math.ceil(this.thumbEl.getTop(true));
30067         
30068         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30069         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30070         
30071         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30072         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30073         
30074         x = x - this.mouseX;
30075         y = y - this.mouseY;
30076         
30077         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30078         var bgY = Math.ceil(y + this.previewEl.getTop(true));
30079         
30080         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30081         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30082         
30083         this.previewEl.setLeft(bgX);
30084         this.previewEl.setTop(bgY);
30085         
30086         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30087         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30088     },
30089     
30090     onMouseUp : function(e)
30091     {   
30092         e.stopEvent();
30093         
30094         this.dragable = false;
30095     },
30096     
30097     onMouseWheel : function(e)
30098     {   
30099         e.stopEvent();
30100         
30101         this.startScale = this.scale;
30102         
30103         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30104         
30105         if(!this.zoomable()){
30106             this.scale = this.startScale;
30107             return;
30108         }
30109         
30110         this.draw();
30111         
30112         return;
30113     },
30114     
30115     zoomable : function()
30116     {
30117         var minScale = this.thumbEl.getWidth() / this.minWidth;
30118         
30119         if(this.minWidth < this.minHeight){
30120             minScale = this.thumbEl.getHeight() / this.minHeight;
30121         }
30122         
30123         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30124         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30125         
30126         if(
30127                 this.isDocument &&
30128                 (this.rotate == 0 || this.rotate == 180) && 
30129                 (
30130                     width > this.imageEl.OriginWidth || 
30131                     height > this.imageEl.OriginHeight ||
30132                     (width < this.minWidth && height < this.minHeight)
30133                 )
30134         ){
30135             return false;
30136         }
30137         
30138         if(
30139                 this.isDocument &&
30140                 (this.rotate == 90 || this.rotate == 270) && 
30141                 (
30142                     width > this.imageEl.OriginWidth || 
30143                     height > this.imageEl.OriginHeight ||
30144                     (width < this.minHeight && height < this.minWidth)
30145                 )
30146         ){
30147             return false;
30148         }
30149         
30150         if(
30151                 !this.isDocument &&
30152                 (this.rotate == 0 || this.rotate == 180) && 
30153                 (
30154                     width < this.minWidth || 
30155                     width > this.imageEl.OriginWidth || 
30156                     height < this.minHeight || 
30157                     height > this.imageEl.OriginHeight
30158                 )
30159         ){
30160             return false;
30161         }
30162         
30163         if(
30164                 !this.isDocument &&
30165                 (this.rotate == 90 || this.rotate == 270) && 
30166                 (
30167                     width < this.minHeight || 
30168                     width > this.imageEl.OriginWidth || 
30169                     height < this.minWidth || 
30170                     height > this.imageEl.OriginHeight
30171                 )
30172         ){
30173             return false;
30174         }
30175         
30176         return true;
30177         
30178     },
30179     
30180     onRotateLeft : function(e)
30181     {   
30182         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30183             
30184             var minScale = this.thumbEl.getWidth() / this.minWidth;
30185             
30186             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30187             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30188             
30189             this.startScale = this.scale;
30190             
30191             while (this.getScaleLevel() < minScale){
30192             
30193                 this.scale = this.scale + 1;
30194                 
30195                 if(!this.zoomable()){
30196                     break;
30197                 }
30198                 
30199                 if(
30200                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30201                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30202                 ){
30203                     continue;
30204                 }
30205                 
30206                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30207
30208                 this.draw();
30209                 
30210                 return;
30211             }
30212             
30213             this.scale = this.startScale;
30214             
30215             this.onRotateFail();
30216             
30217             return false;
30218         }
30219         
30220         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30221
30222         if(this.isDocument){
30223             this.setThumbBoxSize();
30224             this.setThumbBoxPosition();
30225             this.setCanvasPosition();
30226         }
30227         
30228         this.draw();
30229         
30230         this.fireEvent('rotate', this, 'left');
30231         
30232     },
30233     
30234     onRotateRight : function(e)
30235     {
30236         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30237             
30238             var minScale = this.thumbEl.getWidth() / this.minWidth;
30239         
30240             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30241             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30242             
30243             this.startScale = this.scale;
30244             
30245             while (this.getScaleLevel() < minScale){
30246             
30247                 this.scale = this.scale + 1;
30248                 
30249                 if(!this.zoomable()){
30250                     break;
30251                 }
30252                 
30253                 if(
30254                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30255                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30256                 ){
30257                     continue;
30258                 }
30259                 
30260                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30261
30262                 this.draw();
30263                 
30264                 return;
30265             }
30266             
30267             this.scale = this.startScale;
30268             
30269             this.onRotateFail();
30270             
30271             return false;
30272         }
30273         
30274         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30275
30276         if(this.isDocument){
30277             this.setThumbBoxSize();
30278             this.setThumbBoxPosition();
30279             this.setCanvasPosition();
30280         }
30281         
30282         this.draw();
30283         
30284         this.fireEvent('rotate', this, 'right');
30285     },
30286     
30287     onRotateFail : function()
30288     {
30289         this.errorEl.show(true);
30290         
30291         var _this = this;
30292         
30293         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30294     },
30295     
30296     draw : function()
30297     {
30298         this.previewEl.dom.innerHTML = '';
30299         
30300         var canvasEl = document.createElement("canvas");
30301         
30302         var contextEl = canvasEl.getContext("2d");
30303         
30304         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30305         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30306         var center = this.imageEl.OriginWidth / 2;
30307         
30308         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30309             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30310             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30311             center = this.imageEl.OriginHeight / 2;
30312         }
30313         
30314         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30315         
30316         contextEl.translate(center, center);
30317         contextEl.rotate(this.rotate * Math.PI / 180);
30318
30319         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30320         
30321         this.canvasEl = document.createElement("canvas");
30322         
30323         this.contextEl = this.canvasEl.getContext("2d");
30324         
30325         switch (this.rotate) {
30326             case 0 :
30327                 
30328                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30329                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30330                 
30331                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30332                 
30333                 break;
30334             case 90 : 
30335                 
30336                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30337                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30338                 
30339                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30340                     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);
30341                     break;
30342                 }
30343                 
30344                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30345                 
30346                 break;
30347             case 180 :
30348                 
30349                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30350                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30351                 
30352                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30353                     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);
30354                     break;
30355                 }
30356                 
30357                 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);
30358                 
30359                 break;
30360             case 270 :
30361                 
30362                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30363                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30364         
30365                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30366                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30367                     break;
30368                 }
30369                 
30370                 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);
30371                 
30372                 break;
30373             default : 
30374                 break;
30375         }
30376         
30377         this.previewEl.appendChild(this.canvasEl);
30378         
30379         this.setCanvasPosition();
30380     },
30381     
30382     crop : function()
30383     {
30384         if(!this.canvasLoaded){
30385             return;
30386         }
30387         
30388         var imageCanvas = document.createElement("canvas");
30389         
30390         var imageContext = imageCanvas.getContext("2d");
30391         
30392         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30393         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30394         
30395         var center = imageCanvas.width / 2;
30396         
30397         imageContext.translate(center, center);
30398         
30399         imageContext.rotate(this.rotate * Math.PI / 180);
30400         
30401         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30402         
30403         var canvas = document.createElement("canvas");
30404         
30405         var context = canvas.getContext("2d");
30406                 
30407         canvas.width = this.minWidth;
30408         canvas.height = this.minHeight;
30409
30410         switch (this.rotate) {
30411             case 0 :
30412                 
30413                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30414                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30415                 
30416                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30417                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30418                 
30419                 var targetWidth = this.minWidth - 2 * x;
30420                 var targetHeight = this.minHeight - 2 * y;
30421                 
30422                 var scale = 1;
30423                 
30424                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30425                     scale = targetWidth / width;
30426                 }
30427                 
30428                 if(x > 0 && y == 0){
30429                     scale = targetHeight / height;
30430                 }
30431                 
30432                 if(x > 0 && y > 0){
30433                     scale = targetWidth / width;
30434                     
30435                     if(width < height){
30436                         scale = targetHeight / height;
30437                     }
30438                 }
30439                 
30440                 context.scale(scale, scale);
30441                 
30442                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30443                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30444
30445                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30446                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30447
30448                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30449                 
30450                 break;
30451             case 90 : 
30452                 
30453                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30454                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30455                 
30456                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30457                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30458                 
30459                 var targetWidth = this.minWidth - 2 * x;
30460                 var targetHeight = this.minHeight - 2 * y;
30461                 
30462                 var scale = 1;
30463                 
30464                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30465                     scale = targetWidth / width;
30466                 }
30467                 
30468                 if(x > 0 && y == 0){
30469                     scale = targetHeight / height;
30470                 }
30471                 
30472                 if(x > 0 && y > 0){
30473                     scale = targetWidth / width;
30474                     
30475                     if(width < height){
30476                         scale = targetHeight / height;
30477                     }
30478                 }
30479                 
30480                 context.scale(scale, scale);
30481                 
30482                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30483                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30484
30485                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30486                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30487                 
30488                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30489                 
30490                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30491                 
30492                 break;
30493             case 180 :
30494                 
30495                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30496                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30497                 
30498                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30499                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30500                 
30501                 var targetWidth = this.minWidth - 2 * x;
30502                 var targetHeight = this.minHeight - 2 * y;
30503                 
30504                 var scale = 1;
30505                 
30506                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30507                     scale = targetWidth / width;
30508                 }
30509                 
30510                 if(x > 0 && y == 0){
30511                     scale = targetHeight / height;
30512                 }
30513                 
30514                 if(x > 0 && y > 0){
30515                     scale = targetWidth / width;
30516                     
30517                     if(width < height){
30518                         scale = targetHeight / height;
30519                     }
30520                 }
30521                 
30522                 context.scale(scale, scale);
30523                 
30524                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30525                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30526
30527                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30528                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30529
30530                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30531                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30532                 
30533                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30534                 
30535                 break;
30536             case 270 :
30537                 
30538                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30539                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30540                 
30541                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30542                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30543                 
30544                 var targetWidth = this.minWidth - 2 * x;
30545                 var targetHeight = this.minHeight - 2 * y;
30546                 
30547                 var scale = 1;
30548                 
30549                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30550                     scale = targetWidth / width;
30551                 }
30552                 
30553                 if(x > 0 && y == 0){
30554                     scale = targetHeight / height;
30555                 }
30556                 
30557                 if(x > 0 && y > 0){
30558                     scale = targetWidth / width;
30559                     
30560                     if(width < height){
30561                         scale = targetHeight / height;
30562                     }
30563                 }
30564                 
30565                 context.scale(scale, scale);
30566                 
30567                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30568                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30569
30570                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30571                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30572                 
30573                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30574                 
30575                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30576                 
30577                 break;
30578             default : 
30579                 break;
30580         }
30581         
30582         this.cropData = canvas.toDataURL(this.cropType);
30583         
30584         if(this.fireEvent('crop', this, this.cropData) !== false){
30585             this.process(this.file, this.cropData);
30586         }
30587         
30588         return;
30589         
30590     },
30591     
30592     setThumbBoxSize : function()
30593     {
30594         var width, height;
30595         
30596         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30597             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30598             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30599             
30600             this.minWidth = width;
30601             this.minHeight = height;
30602             
30603             if(this.rotate == 90 || this.rotate == 270){
30604                 this.minWidth = height;
30605                 this.minHeight = width;
30606             }
30607         }
30608         
30609         height = 300;
30610         width = Math.ceil(this.minWidth * height / this.minHeight);
30611         
30612         if(this.minWidth > this.minHeight){
30613             width = 300;
30614             height = Math.ceil(this.minHeight * width / this.minWidth);
30615         }
30616         
30617         this.thumbEl.setStyle({
30618             width : width + 'px',
30619             height : height + 'px'
30620         });
30621
30622         return;
30623             
30624     },
30625     
30626     setThumbBoxPosition : function()
30627     {
30628         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30629         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30630         
30631         this.thumbEl.setLeft(x);
30632         this.thumbEl.setTop(y);
30633         
30634     },
30635     
30636     baseRotateLevel : function()
30637     {
30638         this.baseRotate = 1;
30639         
30640         if(
30641                 typeof(this.exif) != 'undefined' &&
30642                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30643                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30644         ){
30645             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30646         }
30647         
30648         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30649         
30650     },
30651     
30652     baseScaleLevel : function()
30653     {
30654         var width, height;
30655         
30656         if(this.isDocument){
30657             
30658             if(this.baseRotate == 6 || this.baseRotate == 8){
30659             
30660                 height = this.thumbEl.getHeight();
30661                 this.baseScale = height / this.imageEl.OriginWidth;
30662
30663                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30664                     width = this.thumbEl.getWidth();
30665                     this.baseScale = width / this.imageEl.OriginHeight;
30666                 }
30667
30668                 return;
30669             }
30670
30671             height = this.thumbEl.getHeight();
30672             this.baseScale = height / this.imageEl.OriginHeight;
30673
30674             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30675                 width = this.thumbEl.getWidth();
30676                 this.baseScale = width / this.imageEl.OriginWidth;
30677             }
30678
30679             return;
30680         }
30681         
30682         if(this.baseRotate == 6 || this.baseRotate == 8){
30683             
30684             width = this.thumbEl.getHeight();
30685             this.baseScale = width / this.imageEl.OriginHeight;
30686             
30687             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30688                 height = this.thumbEl.getWidth();
30689                 this.baseScale = height / this.imageEl.OriginHeight;
30690             }
30691             
30692             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30693                 height = this.thumbEl.getWidth();
30694                 this.baseScale = height / this.imageEl.OriginHeight;
30695                 
30696                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30697                     width = this.thumbEl.getHeight();
30698                     this.baseScale = width / this.imageEl.OriginWidth;
30699                 }
30700             }
30701             
30702             return;
30703         }
30704         
30705         width = this.thumbEl.getWidth();
30706         this.baseScale = width / this.imageEl.OriginWidth;
30707         
30708         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30709             height = this.thumbEl.getHeight();
30710             this.baseScale = height / this.imageEl.OriginHeight;
30711         }
30712         
30713         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30714             
30715             height = this.thumbEl.getHeight();
30716             this.baseScale = height / this.imageEl.OriginHeight;
30717             
30718             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30719                 width = this.thumbEl.getWidth();
30720                 this.baseScale = width / this.imageEl.OriginWidth;
30721             }
30722             
30723         }
30724         
30725         return;
30726     },
30727     
30728     getScaleLevel : function()
30729     {
30730         return this.baseScale * Math.pow(1.1, this.scale);
30731     },
30732     
30733     onTouchStart : function(e)
30734     {
30735         if(!this.canvasLoaded){
30736             this.beforeSelectFile(e);
30737             return;
30738         }
30739         
30740         var touches = e.browserEvent.touches;
30741         
30742         if(!touches){
30743             return;
30744         }
30745         
30746         if(touches.length == 1){
30747             this.onMouseDown(e);
30748             return;
30749         }
30750         
30751         if(touches.length != 2){
30752             return;
30753         }
30754         
30755         var coords = [];
30756         
30757         for(var i = 0, finger; finger = touches[i]; i++){
30758             coords.push(finger.pageX, finger.pageY);
30759         }
30760         
30761         var x = Math.pow(coords[0] - coords[2], 2);
30762         var y = Math.pow(coords[1] - coords[3], 2);
30763         
30764         this.startDistance = Math.sqrt(x + y);
30765         
30766         this.startScale = this.scale;
30767         
30768         this.pinching = true;
30769         this.dragable = false;
30770         
30771     },
30772     
30773     onTouchMove : function(e)
30774     {
30775         if(!this.pinching && !this.dragable){
30776             return;
30777         }
30778         
30779         var touches = e.browserEvent.touches;
30780         
30781         if(!touches){
30782             return;
30783         }
30784         
30785         if(this.dragable){
30786             this.onMouseMove(e);
30787             return;
30788         }
30789         
30790         var coords = [];
30791         
30792         for(var i = 0, finger; finger = touches[i]; i++){
30793             coords.push(finger.pageX, finger.pageY);
30794         }
30795         
30796         var x = Math.pow(coords[0] - coords[2], 2);
30797         var y = Math.pow(coords[1] - coords[3], 2);
30798         
30799         this.endDistance = Math.sqrt(x + y);
30800         
30801         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30802         
30803         if(!this.zoomable()){
30804             this.scale = this.startScale;
30805             return;
30806         }
30807         
30808         this.draw();
30809         
30810     },
30811     
30812     onTouchEnd : function(e)
30813     {
30814         this.pinching = false;
30815         this.dragable = false;
30816         
30817     },
30818     
30819     process : function(file, crop)
30820     {
30821         if(this.loadMask){
30822             this.maskEl.mask(this.loadingText);
30823         }
30824         
30825         this.xhr = new XMLHttpRequest();
30826         
30827         file.xhr = this.xhr;
30828
30829         this.xhr.open(this.method, this.url, true);
30830         
30831         var headers = {
30832             "Accept": "application/json",
30833             "Cache-Control": "no-cache",
30834             "X-Requested-With": "XMLHttpRequest"
30835         };
30836         
30837         for (var headerName in headers) {
30838             var headerValue = headers[headerName];
30839             if (headerValue) {
30840                 this.xhr.setRequestHeader(headerName, headerValue);
30841             }
30842         }
30843         
30844         var _this = this;
30845         
30846         this.xhr.onload = function()
30847         {
30848             _this.xhrOnLoad(_this.xhr);
30849         }
30850         
30851         this.xhr.onerror = function()
30852         {
30853             _this.xhrOnError(_this.xhr);
30854         }
30855         
30856         var formData = new FormData();
30857
30858         formData.append('returnHTML', 'NO');
30859         
30860         if(crop){
30861             formData.append('crop', crop);
30862         }
30863         
30864         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30865             formData.append(this.paramName, file, file.name);
30866         }
30867         
30868         if(typeof(file.filename) != 'undefined'){
30869             formData.append('filename', file.filename);
30870         }
30871         
30872         if(typeof(file.mimetype) != 'undefined'){
30873             formData.append('mimetype', file.mimetype);
30874         }
30875         
30876         if(this.fireEvent('arrange', this, formData) != false){
30877             this.xhr.send(formData);
30878         };
30879     },
30880     
30881     xhrOnLoad : function(xhr)
30882     {
30883         if(this.loadMask){
30884             this.maskEl.unmask();
30885         }
30886         
30887         if (xhr.readyState !== 4) {
30888             this.fireEvent('exception', this, xhr);
30889             return;
30890         }
30891
30892         var response = Roo.decode(xhr.responseText);
30893         
30894         if(!response.success){
30895             this.fireEvent('exception', this, xhr);
30896             return;
30897         }
30898         
30899         var response = Roo.decode(xhr.responseText);
30900         
30901         this.fireEvent('upload', this, response);
30902         
30903     },
30904     
30905     xhrOnError : function()
30906     {
30907         if(this.loadMask){
30908             this.maskEl.unmask();
30909         }
30910         
30911         Roo.log('xhr on error');
30912         
30913         var response = Roo.decode(xhr.responseText);
30914           
30915         Roo.log(response);
30916         
30917     },
30918     
30919     prepare : function(file)
30920     {   
30921         if(this.loadMask){
30922             this.maskEl.mask(this.loadingText);
30923         }
30924         
30925         this.file = false;
30926         this.exif = {};
30927         
30928         if(typeof(file) === 'string'){
30929             this.loadCanvas(file);
30930             return;
30931         }
30932         
30933         if(!file || !this.urlAPI){
30934             return;
30935         }
30936         
30937         this.file = file;
30938         this.cropType = file.type;
30939         
30940         var _this = this;
30941         
30942         if(this.fireEvent('prepare', this, this.file) != false){
30943             
30944             var reader = new FileReader();
30945             
30946             reader.onload = function (e) {
30947                 if (e.target.error) {
30948                     Roo.log(e.target.error);
30949                     return;
30950                 }
30951                 
30952                 var buffer = e.target.result,
30953                     dataView = new DataView(buffer),
30954                     offset = 2,
30955                     maxOffset = dataView.byteLength - 4,
30956                     markerBytes,
30957                     markerLength;
30958                 
30959                 if (dataView.getUint16(0) === 0xffd8) {
30960                     while (offset < maxOffset) {
30961                         markerBytes = dataView.getUint16(offset);
30962                         
30963                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30964                             markerLength = dataView.getUint16(offset + 2) + 2;
30965                             if (offset + markerLength > dataView.byteLength) {
30966                                 Roo.log('Invalid meta data: Invalid segment size.');
30967                                 break;
30968                             }
30969                             
30970                             if(markerBytes == 0xffe1){
30971                                 _this.parseExifData(
30972                                     dataView,
30973                                     offset,
30974                                     markerLength
30975                                 );
30976                             }
30977                             
30978                             offset += markerLength;
30979                             
30980                             continue;
30981                         }
30982                         
30983                         break;
30984                     }
30985                     
30986                 }
30987                 
30988                 var url = _this.urlAPI.createObjectURL(_this.file);
30989                 
30990                 _this.loadCanvas(url);
30991                 
30992                 return;
30993             }
30994             
30995             reader.readAsArrayBuffer(this.file);
30996             
30997         }
30998         
30999     },
31000     
31001     parseExifData : function(dataView, offset, length)
31002     {
31003         var tiffOffset = offset + 10,
31004             littleEndian,
31005             dirOffset;
31006     
31007         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31008             // No Exif data, might be XMP data instead
31009             return;
31010         }
31011         
31012         // Check for the ASCII code for "Exif" (0x45786966):
31013         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31014             // No Exif data, might be XMP data instead
31015             return;
31016         }
31017         if (tiffOffset + 8 > dataView.byteLength) {
31018             Roo.log('Invalid Exif data: Invalid segment size.');
31019             return;
31020         }
31021         // Check for the two null bytes:
31022         if (dataView.getUint16(offset + 8) !== 0x0000) {
31023             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31024             return;
31025         }
31026         // Check the byte alignment:
31027         switch (dataView.getUint16(tiffOffset)) {
31028         case 0x4949:
31029             littleEndian = true;
31030             break;
31031         case 0x4D4D:
31032             littleEndian = false;
31033             break;
31034         default:
31035             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31036             return;
31037         }
31038         // Check for the TIFF tag marker (0x002A):
31039         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31040             Roo.log('Invalid Exif data: Missing TIFF marker.');
31041             return;
31042         }
31043         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31044         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31045         
31046         this.parseExifTags(
31047             dataView,
31048             tiffOffset,
31049             tiffOffset + dirOffset,
31050             littleEndian
31051         );
31052     },
31053     
31054     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31055     {
31056         var tagsNumber,
31057             dirEndOffset,
31058             i;
31059         if (dirOffset + 6 > dataView.byteLength) {
31060             Roo.log('Invalid Exif data: Invalid directory offset.');
31061             return;
31062         }
31063         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31064         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31065         if (dirEndOffset + 4 > dataView.byteLength) {
31066             Roo.log('Invalid Exif data: Invalid directory size.');
31067             return;
31068         }
31069         for (i = 0; i < tagsNumber; i += 1) {
31070             this.parseExifTag(
31071                 dataView,
31072                 tiffOffset,
31073                 dirOffset + 2 + 12 * i, // tag offset
31074                 littleEndian
31075             );
31076         }
31077         // Return the offset to the next directory:
31078         return dataView.getUint32(dirEndOffset, littleEndian);
31079     },
31080     
31081     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31082     {
31083         var tag = dataView.getUint16(offset, littleEndian);
31084         
31085         this.exif[tag] = this.getExifValue(
31086             dataView,
31087             tiffOffset,
31088             offset,
31089             dataView.getUint16(offset + 2, littleEndian), // tag type
31090             dataView.getUint32(offset + 4, littleEndian), // tag length
31091             littleEndian
31092         );
31093     },
31094     
31095     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31096     {
31097         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31098             tagSize,
31099             dataOffset,
31100             values,
31101             i,
31102             str,
31103             c;
31104     
31105         if (!tagType) {
31106             Roo.log('Invalid Exif data: Invalid tag type.');
31107             return;
31108         }
31109         
31110         tagSize = tagType.size * length;
31111         // Determine if the value is contained in the dataOffset bytes,
31112         // or if the value at the dataOffset is a pointer to the actual data:
31113         dataOffset = tagSize > 4 ?
31114                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31115         if (dataOffset + tagSize > dataView.byteLength) {
31116             Roo.log('Invalid Exif data: Invalid data offset.');
31117             return;
31118         }
31119         if (length === 1) {
31120             return tagType.getValue(dataView, dataOffset, littleEndian);
31121         }
31122         values = [];
31123         for (i = 0; i < length; i += 1) {
31124             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31125         }
31126         
31127         if (tagType.ascii) {
31128             str = '';
31129             // Concatenate the chars:
31130             for (i = 0; i < values.length; i += 1) {
31131                 c = values[i];
31132                 // Ignore the terminating NULL byte(s):
31133                 if (c === '\u0000') {
31134                     break;
31135                 }
31136                 str += c;
31137             }
31138             return str;
31139         }
31140         return values;
31141     }
31142     
31143 });
31144
31145 Roo.apply(Roo.bootstrap.UploadCropbox, {
31146     tags : {
31147         'Orientation': 0x0112
31148     },
31149     
31150     Orientation: {
31151             1: 0, //'top-left',
31152 //            2: 'top-right',
31153             3: 180, //'bottom-right',
31154 //            4: 'bottom-left',
31155 //            5: 'left-top',
31156             6: 90, //'right-top',
31157 //            7: 'right-bottom',
31158             8: 270 //'left-bottom'
31159     },
31160     
31161     exifTagTypes : {
31162         // byte, 8-bit unsigned int:
31163         1: {
31164             getValue: function (dataView, dataOffset) {
31165                 return dataView.getUint8(dataOffset);
31166             },
31167             size: 1
31168         },
31169         // ascii, 8-bit byte:
31170         2: {
31171             getValue: function (dataView, dataOffset) {
31172                 return String.fromCharCode(dataView.getUint8(dataOffset));
31173             },
31174             size: 1,
31175             ascii: true
31176         },
31177         // short, 16 bit int:
31178         3: {
31179             getValue: function (dataView, dataOffset, littleEndian) {
31180                 return dataView.getUint16(dataOffset, littleEndian);
31181             },
31182             size: 2
31183         },
31184         // long, 32 bit int:
31185         4: {
31186             getValue: function (dataView, dataOffset, littleEndian) {
31187                 return dataView.getUint32(dataOffset, littleEndian);
31188             },
31189             size: 4
31190         },
31191         // rational = two long values, first is numerator, second is denominator:
31192         5: {
31193             getValue: function (dataView, dataOffset, littleEndian) {
31194                 return dataView.getUint32(dataOffset, littleEndian) /
31195                     dataView.getUint32(dataOffset + 4, littleEndian);
31196             },
31197             size: 8
31198         },
31199         // slong, 32 bit signed int:
31200         9: {
31201             getValue: function (dataView, dataOffset, littleEndian) {
31202                 return dataView.getInt32(dataOffset, littleEndian);
31203             },
31204             size: 4
31205         },
31206         // srational, two slongs, first is numerator, second is denominator:
31207         10: {
31208             getValue: function (dataView, dataOffset, littleEndian) {
31209                 return dataView.getInt32(dataOffset, littleEndian) /
31210                     dataView.getInt32(dataOffset + 4, littleEndian);
31211             },
31212             size: 8
31213         }
31214     },
31215     
31216     footer : {
31217         STANDARD : [
31218             {
31219                 tag : 'div',
31220                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31221                 action : 'rotate-left',
31222                 cn : [
31223                     {
31224                         tag : 'button',
31225                         cls : 'btn btn-default',
31226                         html : '<i class="fa fa-undo"></i>'
31227                     }
31228                 ]
31229             },
31230             {
31231                 tag : 'div',
31232                 cls : 'btn-group roo-upload-cropbox-picture',
31233                 action : 'picture',
31234                 cn : [
31235                     {
31236                         tag : 'button',
31237                         cls : 'btn btn-default',
31238                         html : '<i class="fa fa-picture-o"></i>'
31239                     }
31240                 ]
31241             },
31242             {
31243                 tag : 'div',
31244                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31245                 action : 'rotate-right',
31246                 cn : [
31247                     {
31248                         tag : 'button',
31249                         cls : 'btn btn-default',
31250                         html : '<i class="fa fa-repeat"></i>'
31251                     }
31252                 ]
31253             }
31254         ],
31255         DOCUMENT : [
31256             {
31257                 tag : 'div',
31258                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31259                 action : 'rotate-left',
31260                 cn : [
31261                     {
31262                         tag : 'button',
31263                         cls : 'btn btn-default',
31264                         html : '<i class="fa fa-undo"></i>'
31265                     }
31266                 ]
31267             },
31268             {
31269                 tag : 'div',
31270                 cls : 'btn-group roo-upload-cropbox-download',
31271                 action : 'download',
31272                 cn : [
31273                     {
31274                         tag : 'button',
31275                         cls : 'btn btn-default',
31276                         html : '<i class="fa fa-download"></i>'
31277                     }
31278                 ]
31279             },
31280             {
31281                 tag : 'div',
31282                 cls : 'btn-group roo-upload-cropbox-crop',
31283                 action : 'crop',
31284                 cn : [
31285                     {
31286                         tag : 'button',
31287                         cls : 'btn btn-default',
31288                         html : '<i class="fa fa-crop"></i>'
31289                     }
31290                 ]
31291             },
31292             {
31293                 tag : 'div',
31294                 cls : 'btn-group roo-upload-cropbox-trash',
31295                 action : 'trash',
31296                 cn : [
31297                     {
31298                         tag : 'button',
31299                         cls : 'btn btn-default',
31300                         html : '<i class="fa fa-trash"></i>'
31301                     }
31302                 ]
31303             },
31304             {
31305                 tag : 'div',
31306                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31307                 action : 'rotate-right',
31308                 cn : [
31309                     {
31310                         tag : 'button',
31311                         cls : 'btn btn-default',
31312                         html : '<i class="fa fa-repeat"></i>'
31313                     }
31314                 ]
31315             }
31316         ],
31317         ROTATOR : [
31318             {
31319                 tag : 'div',
31320                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31321                 action : 'rotate-left',
31322                 cn : [
31323                     {
31324                         tag : 'button',
31325                         cls : 'btn btn-default',
31326                         html : '<i class="fa fa-undo"></i>'
31327                     }
31328                 ]
31329             },
31330             {
31331                 tag : 'div',
31332                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31333                 action : 'rotate-right',
31334                 cn : [
31335                     {
31336                         tag : 'button',
31337                         cls : 'btn btn-default',
31338                         html : '<i class="fa fa-repeat"></i>'
31339                     }
31340                 ]
31341             }
31342         ]
31343     }
31344 });
31345
31346 /*
31347 * Licence: LGPL
31348 */
31349
31350 /**
31351  * @class Roo.bootstrap.DocumentManager
31352  * @extends Roo.bootstrap.Component
31353  * Bootstrap DocumentManager class
31354  * @cfg {String} paramName default 'imageUpload'
31355  * @cfg {String} toolTipName default 'filename'
31356  * @cfg {String} method default POST
31357  * @cfg {String} url action url
31358  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31359  * @cfg {Boolean} multiple multiple upload default true
31360  * @cfg {Number} thumbSize default 300
31361  * @cfg {String} fieldLabel
31362  * @cfg {Number} labelWidth default 4
31363  * @cfg {String} labelAlign (left|top) default left
31364  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31365 * @cfg {Number} labellg set the width of label (1-12)
31366  * @cfg {Number} labelmd set the width of label (1-12)
31367  * @cfg {Number} labelsm set the width of label (1-12)
31368  * @cfg {Number} labelxs set the width of label (1-12)
31369  * 
31370  * @constructor
31371  * Create a new DocumentManager
31372  * @param {Object} config The config object
31373  */
31374
31375 Roo.bootstrap.DocumentManager = function(config){
31376     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31377     
31378     this.files = [];
31379     this.delegates = [];
31380     
31381     this.addEvents({
31382         /**
31383          * @event initial
31384          * Fire when initial the DocumentManager
31385          * @param {Roo.bootstrap.DocumentManager} this
31386          */
31387         "initial" : true,
31388         /**
31389          * @event inspect
31390          * inspect selected file
31391          * @param {Roo.bootstrap.DocumentManager} this
31392          * @param {File} file
31393          */
31394         "inspect" : true,
31395         /**
31396          * @event exception
31397          * Fire when xhr load exception
31398          * @param {Roo.bootstrap.DocumentManager} this
31399          * @param {XMLHttpRequest} xhr
31400          */
31401         "exception" : true,
31402         /**
31403          * @event afterupload
31404          * Fire when xhr load exception
31405          * @param {Roo.bootstrap.DocumentManager} this
31406          * @param {XMLHttpRequest} xhr
31407          */
31408         "afterupload" : true,
31409         /**
31410          * @event prepare
31411          * prepare the form data
31412          * @param {Roo.bootstrap.DocumentManager} this
31413          * @param {Object} formData
31414          */
31415         "prepare" : true,
31416         /**
31417          * @event remove
31418          * Fire when remove the file
31419          * @param {Roo.bootstrap.DocumentManager} this
31420          * @param {Object} file
31421          */
31422         "remove" : true,
31423         /**
31424          * @event refresh
31425          * Fire after refresh the file
31426          * @param {Roo.bootstrap.DocumentManager} this
31427          */
31428         "refresh" : true,
31429         /**
31430          * @event click
31431          * Fire after click the image
31432          * @param {Roo.bootstrap.DocumentManager} this
31433          * @param {Object} file
31434          */
31435         "click" : true,
31436         /**
31437          * @event edit
31438          * Fire when upload a image and editable set to true
31439          * @param {Roo.bootstrap.DocumentManager} this
31440          * @param {Object} file
31441          */
31442         "edit" : true,
31443         /**
31444          * @event beforeselectfile
31445          * Fire before select file
31446          * @param {Roo.bootstrap.DocumentManager} this
31447          */
31448         "beforeselectfile" : true,
31449         /**
31450          * @event process
31451          * Fire before process file
31452          * @param {Roo.bootstrap.DocumentManager} this
31453          * @param {Object} file
31454          */
31455         "process" : true,
31456         /**
31457          * @event previewrendered
31458          * Fire when preview rendered
31459          * @param {Roo.bootstrap.DocumentManager} this
31460          * @param {Object} file
31461          */
31462         "previewrendered" : true,
31463         /**
31464          */
31465         "previewResize" : true
31466         
31467     });
31468 };
31469
31470 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31471     
31472     boxes : 0,
31473     inputName : '',
31474     thumbSize : 300,
31475     multiple : true,
31476     files : false,
31477     method : 'POST',
31478     url : '',
31479     paramName : 'imageUpload',
31480     toolTipName : 'filename',
31481     fieldLabel : '',
31482     labelWidth : 4,
31483     labelAlign : 'left',
31484     editable : true,
31485     delegates : false,
31486     xhr : false, 
31487     
31488     labellg : 0,
31489     labelmd : 0,
31490     labelsm : 0,
31491     labelxs : 0,
31492     
31493     getAutoCreate : function()
31494     {   
31495         var managerWidget = {
31496             tag : 'div',
31497             cls : 'roo-document-manager',
31498             cn : [
31499                 {
31500                     tag : 'input',
31501                     cls : 'roo-document-manager-selector',
31502                     type : 'file'
31503                 },
31504                 {
31505                     tag : 'div',
31506                     cls : 'roo-document-manager-uploader',
31507                     cn : [
31508                         {
31509                             tag : 'div',
31510                             cls : 'roo-document-manager-upload-btn',
31511                             html : '<i class="fa fa-plus"></i>'
31512                         }
31513                     ]
31514                     
31515                 }
31516             ]
31517         };
31518         
31519         var content = [
31520             {
31521                 tag : 'div',
31522                 cls : 'column col-md-12',
31523                 cn : managerWidget
31524             }
31525         ];
31526         
31527         if(this.fieldLabel.length){
31528             
31529             content = [
31530                 {
31531                     tag : 'div',
31532                     cls : 'column col-md-12',
31533                     html : this.fieldLabel
31534                 },
31535                 {
31536                     tag : 'div',
31537                     cls : 'column col-md-12',
31538                     cn : managerWidget
31539                 }
31540             ];
31541
31542             if(this.labelAlign == 'left'){
31543                 content = [
31544                     {
31545                         tag : 'div',
31546                         cls : 'column',
31547                         html : this.fieldLabel
31548                     },
31549                     {
31550                         tag : 'div',
31551                         cls : 'column',
31552                         cn : managerWidget
31553                     }
31554                 ];
31555                 
31556                 if(this.labelWidth > 12){
31557                     content[0].style = "width: " + this.labelWidth + 'px';
31558                 }
31559
31560                 if(this.labelWidth < 13 && this.labelmd == 0){
31561                     this.labelmd = this.labelWidth;
31562                 }
31563
31564                 if(this.labellg > 0){
31565                     content[0].cls += ' col-lg-' + this.labellg;
31566                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31567                 }
31568
31569                 if(this.labelmd > 0){
31570                     content[0].cls += ' col-md-' + this.labelmd;
31571                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31572                 }
31573
31574                 if(this.labelsm > 0){
31575                     content[0].cls += ' col-sm-' + this.labelsm;
31576                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31577                 }
31578
31579                 if(this.labelxs > 0){
31580                     content[0].cls += ' col-xs-' + this.labelxs;
31581                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31582                 }
31583                 
31584             }
31585         }
31586         
31587         var cfg = {
31588             tag : 'div',
31589             cls : 'row clearfix',
31590             cn : content
31591         };
31592         
31593         return cfg;
31594         
31595     },
31596     
31597     initEvents : function()
31598     {
31599         this.managerEl = this.el.select('.roo-document-manager', true).first();
31600         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31601         
31602         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31603         this.selectorEl.hide();
31604         
31605         if(this.multiple){
31606             this.selectorEl.attr('multiple', 'multiple');
31607         }
31608         
31609         this.selectorEl.on('change', this.onFileSelected, this);
31610         
31611         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31612         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31613         
31614         this.uploader.on('click', this.onUploaderClick, this);
31615         
31616         this.renderProgressDialog();
31617         
31618         var _this = this;
31619         
31620         window.addEventListener("resize", function() { _this.refresh(); } );
31621         
31622         this.fireEvent('initial', this);
31623     },
31624     
31625     renderProgressDialog : function()
31626     {
31627         var _this = this;
31628         
31629         this.progressDialog = new Roo.bootstrap.Modal({
31630             cls : 'roo-document-manager-progress-dialog',
31631             allow_close : false,
31632             animate : false,
31633             title : '',
31634             buttons : [
31635                 {
31636                     name  :'cancel',
31637                     weight : 'danger',
31638                     html : 'Cancel'
31639                 }
31640             ], 
31641             listeners : { 
31642                 btnclick : function() {
31643                     _this.uploadCancel();
31644                     this.hide();
31645                 }
31646             }
31647         });
31648          
31649         this.progressDialog.render(Roo.get(document.body));
31650          
31651         this.progress = new Roo.bootstrap.Progress({
31652             cls : 'roo-document-manager-progress',
31653             active : true,
31654             striped : true
31655         });
31656         
31657         this.progress.render(this.progressDialog.getChildContainer());
31658         
31659         this.progressBar = new Roo.bootstrap.ProgressBar({
31660             cls : 'roo-document-manager-progress-bar',
31661             aria_valuenow : 0,
31662             aria_valuemin : 0,
31663             aria_valuemax : 12,
31664             panel : 'success'
31665         });
31666         
31667         this.progressBar.render(this.progress.getChildContainer());
31668     },
31669     
31670     onUploaderClick : function(e)
31671     {
31672         e.preventDefault();
31673      
31674         if(this.fireEvent('beforeselectfile', this) != false){
31675             this.selectorEl.dom.click();
31676         }
31677         
31678     },
31679     
31680     onFileSelected : function(e)
31681     {
31682         e.preventDefault();
31683         
31684         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31685             return;
31686         }
31687         
31688         Roo.each(this.selectorEl.dom.files, function(file){
31689             if(this.fireEvent('inspect', this, file) != false){
31690                 this.files.push(file);
31691             }
31692         }, this);
31693         
31694         this.queue();
31695         
31696     },
31697     
31698     queue : function()
31699     {
31700         this.selectorEl.dom.value = '';
31701         
31702         if(!this.files || !this.files.length){
31703             return;
31704         }
31705         
31706         if(this.boxes > 0 && this.files.length > this.boxes){
31707             this.files = this.files.slice(0, this.boxes);
31708         }
31709         
31710         this.uploader.show();
31711         
31712         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31713             this.uploader.hide();
31714         }
31715         
31716         var _this = this;
31717         
31718         var files = [];
31719         
31720         var docs = [];
31721         
31722         Roo.each(this.files, function(file){
31723             
31724             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31725                 var f = this.renderPreview(file);
31726                 files.push(f);
31727                 return;
31728             }
31729             
31730             if(file.type.indexOf('image') != -1){
31731                 this.delegates.push(
31732                     (function(){
31733                         _this.process(file);
31734                     }).createDelegate(this)
31735                 );
31736         
31737                 return;
31738             }
31739             
31740             docs.push(
31741                 (function(){
31742                     _this.process(file);
31743                 }).createDelegate(this)
31744             );
31745             
31746         }, this);
31747         
31748         this.files = files;
31749         
31750         this.delegates = this.delegates.concat(docs);
31751         
31752         if(!this.delegates.length){
31753             this.refresh();
31754             return;
31755         }
31756         
31757         this.progressBar.aria_valuemax = this.delegates.length;
31758         
31759         this.arrange();
31760         
31761         return;
31762     },
31763     
31764     arrange : function()
31765     {
31766         if(!this.delegates.length){
31767             this.progressDialog.hide();
31768             this.refresh();
31769             return;
31770         }
31771         
31772         var delegate = this.delegates.shift();
31773         
31774         this.progressDialog.show();
31775         
31776         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31777         
31778         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31779         
31780         delegate();
31781     },
31782     
31783     refresh : function()
31784     {
31785         this.uploader.show();
31786         
31787         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31788             this.uploader.hide();
31789         }
31790         
31791         Roo.isTouch ? this.closable(false) : this.closable(true);
31792         
31793         this.fireEvent('refresh', this);
31794     },
31795     
31796     onRemove : function(e, el, o)
31797     {
31798         e.preventDefault();
31799         
31800         this.fireEvent('remove', this, o);
31801         
31802     },
31803     
31804     remove : function(o)
31805     {
31806         var files = [];
31807         
31808         Roo.each(this.files, function(file){
31809             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31810                 files.push(file);
31811                 return;
31812             }
31813
31814             o.target.remove();
31815
31816         }, this);
31817         
31818         this.files = files;
31819         
31820         this.refresh();
31821     },
31822     
31823     clear : function()
31824     {
31825         Roo.each(this.files, function(file){
31826             if(!file.target){
31827                 return;
31828             }
31829             
31830             file.target.remove();
31831
31832         }, this);
31833         
31834         this.files = [];
31835         
31836         this.refresh();
31837     },
31838     
31839     onClick : function(e, el, o)
31840     {
31841         e.preventDefault();
31842         
31843         this.fireEvent('click', this, o);
31844         
31845     },
31846     
31847     closable : function(closable)
31848     {
31849         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31850             
31851             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31852             
31853             if(closable){
31854                 el.show();
31855                 return;
31856             }
31857             
31858             el.hide();
31859             
31860         }, this);
31861     },
31862     
31863     xhrOnLoad : function(xhr)
31864     {
31865         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31866             el.remove();
31867         }, this);
31868         
31869         if (xhr.readyState !== 4) {
31870             this.arrange();
31871             this.fireEvent('exception', this, xhr);
31872             return;
31873         }
31874
31875         var response = Roo.decode(xhr.responseText);
31876         
31877         if(!response.success){
31878             this.arrange();
31879             this.fireEvent('exception', this, xhr);
31880             return;
31881         }
31882         
31883         var file = this.renderPreview(response.data);
31884         
31885         this.files.push(file);
31886         
31887         this.arrange();
31888         
31889         this.fireEvent('afterupload', this, xhr);
31890         
31891     },
31892     
31893     xhrOnError : function(xhr)
31894     {
31895         Roo.log('xhr on error');
31896         
31897         var response = Roo.decode(xhr.responseText);
31898           
31899         Roo.log(response);
31900         
31901         this.arrange();
31902     },
31903     
31904     process : function(file)
31905     {
31906         if(this.fireEvent('process', this, file) !== false){
31907             if(this.editable && file.type.indexOf('image') != -1){
31908                 this.fireEvent('edit', this, file);
31909                 return;
31910             }
31911
31912             this.uploadStart(file, false);
31913
31914             return;
31915         }
31916         
31917     },
31918     
31919     uploadStart : function(file, crop)
31920     {
31921         this.xhr = new XMLHttpRequest();
31922         
31923         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31924             this.arrange();
31925             return;
31926         }
31927         
31928         file.xhr = this.xhr;
31929             
31930         this.managerEl.createChild({
31931             tag : 'div',
31932             cls : 'roo-document-manager-loading',
31933             cn : [
31934                 {
31935                     tag : 'div',
31936                     tooltip : file.name,
31937                     cls : 'roo-document-manager-thumb',
31938                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31939                 }
31940             ]
31941
31942         });
31943
31944         this.xhr.open(this.method, this.url, true);
31945         
31946         var headers = {
31947             "Accept": "application/json",
31948             "Cache-Control": "no-cache",
31949             "X-Requested-With": "XMLHttpRequest"
31950         };
31951         
31952         for (var headerName in headers) {
31953             var headerValue = headers[headerName];
31954             if (headerValue) {
31955                 this.xhr.setRequestHeader(headerName, headerValue);
31956             }
31957         }
31958         
31959         var _this = this;
31960         
31961         this.xhr.onload = function()
31962         {
31963             _this.xhrOnLoad(_this.xhr);
31964         }
31965         
31966         this.xhr.onerror = function()
31967         {
31968             _this.xhrOnError(_this.xhr);
31969         }
31970         
31971         var formData = new FormData();
31972
31973         formData.append('returnHTML', 'NO');
31974         
31975         if(crop){
31976             formData.append('crop', crop);
31977         }
31978         
31979         formData.append(this.paramName, file, file.name);
31980         
31981         var options = {
31982             file : file, 
31983             manually : false
31984         };
31985         
31986         if(this.fireEvent('prepare', this, formData, options) != false){
31987             
31988             if(options.manually){
31989                 return;
31990             }
31991             
31992             this.xhr.send(formData);
31993             return;
31994         };
31995         
31996         this.uploadCancel();
31997     },
31998     
31999     uploadCancel : function()
32000     {
32001         if (this.xhr) {
32002             this.xhr.abort();
32003         }
32004         
32005         this.delegates = [];
32006         
32007         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32008             el.remove();
32009         }, this);
32010         
32011         this.arrange();
32012     },
32013     
32014     renderPreview : function(file)
32015     {
32016         if(typeof(file.target) != 'undefined' && file.target){
32017             return file;
32018         }
32019         
32020         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32021         
32022         var previewEl = this.managerEl.createChild({
32023             tag : 'div',
32024             cls : 'roo-document-manager-preview',
32025             cn : [
32026                 {
32027                     tag : 'div',
32028                     tooltip : file[this.toolTipName],
32029                     cls : 'roo-document-manager-thumb',
32030                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32031                 },
32032                 {
32033                     tag : 'button',
32034                     cls : 'close',
32035                     html : '<i class="fa fa-times-circle"></i>'
32036                 }
32037             ]
32038         });
32039
32040         var close = previewEl.select('button.close', true).first();
32041
32042         close.on('click', this.onRemove, this, file);
32043
32044         file.target = previewEl;
32045
32046         var image = previewEl.select('img', true).first();
32047         
32048         var _this = this;
32049         
32050         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32051         
32052         image.on('click', this.onClick, this, file);
32053         
32054         this.fireEvent('previewrendered', this, file);
32055         
32056         return file;
32057         
32058     },
32059     
32060     onPreviewLoad : function(file, image)
32061     {
32062         if(typeof(file.target) == 'undefined' || !file.target){
32063             return;
32064         }
32065         
32066         var width = image.dom.naturalWidth || image.dom.width;
32067         var height = image.dom.naturalHeight || image.dom.height;
32068         
32069         if(!this.previewResize) {
32070             return;
32071         }
32072         
32073         if(width > height){
32074             file.target.addClass('wide');
32075             return;
32076         }
32077         
32078         file.target.addClass('tall');
32079         return;
32080         
32081     },
32082     
32083     uploadFromSource : function(file, crop)
32084     {
32085         this.xhr = new XMLHttpRequest();
32086         
32087         this.managerEl.createChild({
32088             tag : 'div',
32089             cls : 'roo-document-manager-loading',
32090             cn : [
32091                 {
32092                     tag : 'div',
32093                     tooltip : file.name,
32094                     cls : 'roo-document-manager-thumb',
32095                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32096                 }
32097             ]
32098
32099         });
32100
32101         this.xhr.open(this.method, this.url, true);
32102         
32103         var headers = {
32104             "Accept": "application/json",
32105             "Cache-Control": "no-cache",
32106             "X-Requested-With": "XMLHttpRequest"
32107         };
32108         
32109         for (var headerName in headers) {
32110             var headerValue = headers[headerName];
32111             if (headerValue) {
32112                 this.xhr.setRequestHeader(headerName, headerValue);
32113             }
32114         }
32115         
32116         var _this = this;
32117         
32118         this.xhr.onload = function()
32119         {
32120             _this.xhrOnLoad(_this.xhr);
32121         }
32122         
32123         this.xhr.onerror = function()
32124         {
32125             _this.xhrOnError(_this.xhr);
32126         }
32127         
32128         var formData = new FormData();
32129
32130         formData.append('returnHTML', 'NO');
32131         
32132         formData.append('crop', crop);
32133         
32134         if(typeof(file.filename) != 'undefined'){
32135             formData.append('filename', file.filename);
32136         }
32137         
32138         if(typeof(file.mimetype) != 'undefined'){
32139             formData.append('mimetype', file.mimetype);
32140         }
32141         
32142         Roo.log(formData);
32143         
32144         if(this.fireEvent('prepare', this, formData) != false){
32145             this.xhr.send(formData);
32146         };
32147     }
32148 });
32149
32150 /*
32151 * Licence: LGPL
32152 */
32153
32154 /**
32155  * @class Roo.bootstrap.DocumentViewer
32156  * @extends Roo.bootstrap.Component
32157  * Bootstrap DocumentViewer class
32158  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32159  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32160  * 
32161  * @constructor
32162  * Create a new DocumentViewer
32163  * @param {Object} config The config object
32164  */
32165
32166 Roo.bootstrap.DocumentViewer = function(config){
32167     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32168     
32169     this.addEvents({
32170         /**
32171          * @event initial
32172          * Fire after initEvent
32173          * @param {Roo.bootstrap.DocumentViewer} this
32174          */
32175         "initial" : true,
32176         /**
32177          * @event click
32178          * Fire after click
32179          * @param {Roo.bootstrap.DocumentViewer} this
32180          */
32181         "click" : true,
32182         /**
32183          * @event download
32184          * Fire after download button
32185          * @param {Roo.bootstrap.DocumentViewer} this
32186          */
32187         "download" : true,
32188         /**
32189          * @event trash
32190          * Fire after trash button
32191          * @param {Roo.bootstrap.DocumentViewer} this
32192          */
32193         "trash" : true
32194         
32195     });
32196 };
32197
32198 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32199     
32200     showDownload : true,
32201     
32202     showTrash : true,
32203     
32204     getAutoCreate : function()
32205     {
32206         var cfg = {
32207             tag : 'div',
32208             cls : 'roo-document-viewer',
32209             cn : [
32210                 {
32211                     tag : 'div',
32212                     cls : 'roo-document-viewer-body',
32213                     cn : [
32214                         {
32215                             tag : 'div',
32216                             cls : 'roo-document-viewer-thumb',
32217                             cn : [
32218                                 {
32219                                     tag : 'img',
32220                                     cls : 'roo-document-viewer-image'
32221                                 }
32222                             ]
32223                         }
32224                     ]
32225                 },
32226                 {
32227                     tag : 'div',
32228                     cls : 'roo-document-viewer-footer',
32229                     cn : {
32230                         tag : 'div',
32231                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32232                         cn : [
32233                             {
32234                                 tag : 'div',
32235                                 cls : 'btn-group roo-document-viewer-download',
32236                                 cn : [
32237                                     {
32238                                         tag : 'button',
32239                                         cls : 'btn btn-default',
32240                                         html : '<i class="fa fa-download"></i>'
32241                                     }
32242                                 ]
32243                             },
32244                             {
32245                                 tag : 'div',
32246                                 cls : 'btn-group roo-document-viewer-trash',
32247                                 cn : [
32248                                     {
32249                                         tag : 'button',
32250                                         cls : 'btn btn-default',
32251                                         html : '<i class="fa fa-trash"></i>'
32252                                     }
32253                                 ]
32254                             }
32255                         ]
32256                     }
32257                 }
32258             ]
32259         };
32260         
32261         return cfg;
32262     },
32263     
32264     initEvents : function()
32265     {
32266         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32267         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32268         
32269         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32270         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32271         
32272         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32273         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32274         
32275         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32276         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32277         
32278         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32279         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32280         
32281         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32282         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32283         
32284         this.bodyEl.on('click', this.onClick, this);
32285         this.downloadBtn.on('click', this.onDownload, this);
32286         this.trashBtn.on('click', this.onTrash, this);
32287         
32288         this.downloadBtn.hide();
32289         this.trashBtn.hide();
32290         
32291         if(this.showDownload){
32292             this.downloadBtn.show();
32293         }
32294         
32295         if(this.showTrash){
32296             this.trashBtn.show();
32297         }
32298         
32299         if(!this.showDownload && !this.showTrash) {
32300             this.footerEl.hide();
32301         }
32302         
32303     },
32304     
32305     initial : function()
32306     {
32307         this.fireEvent('initial', this);
32308         
32309     },
32310     
32311     onClick : function(e)
32312     {
32313         e.preventDefault();
32314         
32315         this.fireEvent('click', this);
32316     },
32317     
32318     onDownload : function(e)
32319     {
32320         e.preventDefault();
32321         
32322         this.fireEvent('download', this);
32323     },
32324     
32325     onTrash : function(e)
32326     {
32327         e.preventDefault();
32328         
32329         this.fireEvent('trash', this);
32330     }
32331     
32332 });
32333 /*
32334  * - LGPL
32335  *
32336  * nav progress bar
32337  * 
32338  */
32339
32340 /**
32341  * @class Roo.bootstrap.NavProgressBar
32342  * @extends Roo.bootstrap.Component
32343  * Bootstrap NavProgressBar class
32344  * 
32345  * @constructor
32346  * Create a new nav progress bar
32347  * @param {Object} config The config object
32348  */
32349
32350 Roo.bootstrap.NavProgressBar = function(config){
32351     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32352
32353     this.bullets = this.bullets || [];
32354    
32355 //    Roo.bootstrap.NavProgressBar.register(this);
32356      this.addEvents({
32357         /**
32358              * @event changed
32359              * Fires when the active item changes
32360              * @param {Roo.bootstrap.NavProgressBar} this
32361              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32362              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32363          */
32364         'changed': true
32365      });
32366     
32367 };
32368
32369 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32370     
32371     bullets : [],
32372     barItems : [],
32373     
32374     getAutoCreate : function()
32375     {
32376         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32377         
32378         cfg = {
32379             tag : 'div',
32380             cls : 'roo-navigation-bar-group',
32381             cn : [
32382                 {
32383                     tag : 'div',
32384                     cls : 'roo-navigation-top-bar'
32385                 },
32386                 {
32387                     tag : 'div',
32388                     cls : 'roo-navigation-bullets-bar',
32389                     cn : [
32390                         {
32391                             tag : 'ul',
32392                             cls : 'roo-navigation-bar'
32393                         }
32394                     ]
32395                 },
32396                 
32397                 {
32398                     tag : 'div',
32399                     cls : 'roo-navigation-bottom-bar'
32400                 }
32401             ]
32402             
32403         };
32404         
32405         return cfg;
32406         
32407     },
32408     
32409     initEvents: function() 
32410     {
32411         
32412     },
32413     
32414     onRender : function(ct, position) 
32415     {
32416         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32417         
32418         if(this.bullets.length){
32419             Roo.each(this.bullets, function(b){
32420                this.addItem(b);
32421             }, this);
32422         }
32423         
32424         this.format();
32425         
32426     },
32427     
32428     addItem : function(cfg)
32429     {
32430         var item = new Roo.bootstrap.NavProgressItem(cfg);
32431         
32432         item.parentId = this.id;
32433         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32434         
32435         if(cfg.html){
32436             var top = new Roo.bootstrap.Element({
32437                 tag : 'div',
32438                 cls : 'roo-navigation-bar-text'
32439             });
32440             
32441             var bottom = new Roo.bootstrap.Element({
32442                 tag : 'div',
32443                 cls : 'roo-navigation-bar-text'
32444             });
32445             
32446             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32447             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32448             
32449             var topText = new Roo.bootstrap.Element({
32450                 tag : 'span',
32451                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32452             });
32453             
32454             var bottomText = new Roo.bootstrap.Element({
32455                 tag : 'span',
32456                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32457             });
32458             
32459             topText.onRender(top.el, null);
32460             bottomText.onRender(bottom.el, null);
32461             
32462             item.topEl = top;
32463             item.bottomEl = bottom;
32464         }
32465         
32466         this.barItems.push(item);
32467         
32468         return item;
32469     },
32470     
32471     getActive : function()
32472     {
32473         var active = false;
32474         
32475         Roo.each(this.barItems, function(v){
32476             
32477             if (!v.isActive()) {
32478                 return;
32479             }
32480             
32481             active = v;
32482             return false;
32483             
32484         });
32485         
32486         return active;
32487     },
32488     
32489     setActiveItem : function(item)
32490     {
32491         var prev = false;
32492         
32493         Roo.each(this.barItems, function(v){
32494             if (v.rid == item.rid) {
32495                 return ;
32496             }
32497             
32498             if (v.isActive()) {
32499                 v.setActive(false);
32500                 prev = v;
32501             }
32502         });
32503
32504         item.setActive(true);
32505         
32506         this.fireEvent('changed', this, item, prev);
32507     },
32508     
32509     getBarItem: function(rid)
32510     {
32511         var ret = false;
32512         
32513         Roo.each(this.barItems, function(e) {
32514             if (e.rid != rid) {
32515                 return;
32516             }
32517             
32518             ret =  e;
32519             return false;
32520         });
32521         
32522         return ret;
32523     },
32524     
32525     indexOfItem : function(item)
32526     {
32527         var index = false;
32528         
32529         Roo.each(this.barItems, function(v, i){
32530             
32531             if (v.rid != item.rid) {
32532                 return;
32533             }
32534             
32535             index = i;
32536             return false
32537         });
32538         
32539         return index;
32540     },
32541     
32542     setActiveNext : function()
32543     {
32544         var i = this.indexOfItem(this.getActive());
32545         
32546         if (i > this.barItems.length) {
32547             return;
32548         }
32549         
32550         this.setActiveItem(this.barItems[i+1]);
32551     },
32552     
32553     setActivePrev : function()
32554     {
32555         var i = this.indexOfItem(this.getActive());
32556         
32557         if (i  < 1) {
32558             return;
32559         }
32560         
32561         this.setActiveItem(this.barItems[i-1]);
32562     },
32563     
32564     format : function()
32565     {
32566         if(!this.barItems.length){
32567             return;
32568         }
32569      
32570         var width = 100 / this.barItems.length;
32571         
32572         Roo.each(this.barItems, function(i){
32573             i.el.setStyle('width', width + '%');
32574             i.topEl.el.setStyle('width', width + '%');
32575             i.bottomEl.el.setStyle('width', width + '%');
32576         }, this);
32577         
32578     }
32579     
32580 });
32581 /*
32582  * - LGPL
32583  *
32584  * Nav Progress Item
32585  * 
32586  */
32587
32588 /**
32589  * @class Roo.bootstrap.NavProgressItem
32590  * @extends Roo.bootstrap.Component
32591  * Bootstrap NavProgressItem class
32592  * @cfg {String} rid the reference id
32593  * @cfg {Boolean} active (true|false) Is item active default false
32594  * @cfg {Boolean} disabled (true|false) Is item active default false
32595  * @cfg {String} html
32596  * @cfg {String} position (top|bottom) text position default bottom
32597  * @cfg {String} icon show icon instead of number
32598  * 
32599  * @constructor
32600  * Create a new NavProgressItem
32601  * @param {Object} config The config object
32602  */
32603 Roo.bootstrap.NavProgressItem = function(config){
32604     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32605     this.addEvents({
32606         // raw events
32607         /**
32608          * @event click
32609          * The raw click event for the entire grid.
32610          * @param {Roo.bootstrap.NavProgressItem} this
32611          * @param {Roo.EventObject} e
32612          */
32613         "click" : true
32614     });
32615    
32616 };
32617
32618 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32619     
32620     rid : '',
32621     active : false,
32622     disabled : false,
32623     html : '',
32624     position : 'bottom',
32625     icon : false,
32626     
32627     getAutoCreate : function()
32628     {
32629         var iconCls = 'roo-navigation-bar-item-icon';
32630         
32631         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32632         
32633         var cfg = {
32634             tag: 'li',
32635             cls: 'roo-navigation-bar-item',
32636             cn : [
32637                 {
32638                     tag : 'i',
32639                     cls : iconCls
32640                 }
32641             ]
32642         };
32643         
32644         if(this.active){
32645             cfg.cls += ' active';
32646         }
32647         if(this.disabled){
32648             cfg.cls += ' disabled';
32649         }
32650         
32651         return cfg;
32652     },
32653     
32654     disable : function()
32655     {
32656         this.setDisabled(true);
32657     },
32658     
32659     enable : function()
32660     {
32661         this.setDisabled(false);
32662     },
32663     
32664     initEvents: function() 
32665     {
32666         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32667         
32668         this.iconEl.on('click', this.onClick, this);
32669     },
32670     
32671     onClick : function(e)
32672     {
32673         e.preventDefault();
32674         
32675         if(this.disabled){
32676             return;
32677         }
32678         
32679         if(this.fireEvent('click', this, e) === false){
32680             return;
32681         };
32682         
32683         this.parent().setActiveItem(this);
32684     },
32685     
32686     isActive: function () 
32687     {
32688         return this.active;
32689     },
32690     
32691     setActive : function(state)
32692     {
32693         if(this.active == state){
32694             return;
32695         }
32696         
32697         this.active = state;
32698         
32699         if (state) {
32700             this.el.addClass('active');
32701             return;
32702         }
32703         
32704         this.el.removeClass('active');
32705         
32706         return;
32707     },
32708     
32709     setDisabled : function(state)
32710     {
32711         if(this.disabled == state){
32712             return;
32713         }
32714         
32715         this.disabled = state;
32716         
32717         if (state) {
32718             this.el.addClass('disabled');
32719             return;
32720         }
32721         
32722         this.el.removeClass('disabled');
32723     },
32724     
32725     tooltipEl : function()
32726     {
32727         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32728     }
32729 });
32730  
32731
32732  /*
32733  * - LGPL
32734  *
32735  * FieldLabel
32736  * 
32737  */
32738
32739 /**
32740  * @class Roo.bootstrap.FieldLabel
32741  * @extends Roo.bootstrap.Component
32742  * Bootstrap FieldLabel class
32743  * @cfg {String} html contents of the element
32744  * @cfg {String} tag tag of the element default label
32745  * @cfg {String} cls class of the element
32746  * @cfg {String} target label target 
32747  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32748  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32749  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32750  * @cfg {String} iconTooltip default "This field is required"
32751  * @cfg {String} indicatorpos (left|right) default left
32752  * 
32753  * @constructor
32754  * Create a new FieldLabel
32755  * @param {Object} config The config object
32756  */
32757
32758 Roo.bootstrap.FieldLabel = function(config){
32759     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32760     
32761     this.addEvents({
32762             /**
32763              * @event invalid
32764              * Fires after the field has been marked as invalid.
32765              * @param {Roo.form.FieldLabel} this
32766              * @param {String} msg The validation message
32767              */
32768             invalid : true,
32769             /**
32770              * @event valid
32771              * Fires after the field has been validated with no errors.
32772              * @param {Roo.form.FieldLabel} this
32773              */
32774             valid : true
32775         });
32776 };
32777
32778 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32779     
32780     tag: 'label',
32781     cls: '',
32782     html: '',
32783     target: '',
32784     allowBlank : true,
32785     invalidClass : 'has-warning',
32786     validClass : 'has-success',
32787     iconTooltip : 'This field is required',
32788     indicatorpos : 'left',
32789     
32790     getAutoCreate : function(){
32791         
32792         var cls = "";
32793         if (!this.allowBlank) {
32794             cls  = "visible";
32795         }
32796         
32797         var cfg = {
32798             tag : this.tag,
32799             cls : 'roo-bootstrap-field-label ' + this.cls,
32800             for : this.target,
32801             cn : [
32802                 {
32803                     tag : 'i',
32804                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32805                     tooltip : this.iconTooltip
32806                 },
32807                 {
32808                     tag : 'span',
32809                     html : this.html
32810                 }
32811             ] 
32812         };
32813         
32814         if(this.indicatorpos == 'right'){
32815             var cfg = {
32816                 tag : this.tag,
32817                 cls : 'roo-bootstrap-field-label ' + this.cls,
32818                 for : this.target,
32819                 cn : [
32820                     {
32821                         tag : 'span',
32822                         html : this.html
32823                     },
32824                     {
32825                         tag : 'i',
32826                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32827                         tooltip : this.iconTooltip
32828                     }
32829                 ] 
32830             };
32831         }
32832         
32833         return cfg;
32834     },
32835     
32836     initEvents: function() 
32837     {
32838         Roo.bootstrap.Element.superclass.initEvents.call(this);
32839         
32840         this.indicator = this.indicatorEl();
32841         
32842         if(this.indicator){
32843             this.indicator.removeClass('visible');
32844             this.indicator.addClass('invisible');
32845         }
32846         
32847         Roo.bootstrap.FieldLabel.register(this);
32848     },
32849     
32850     indicatorEl : function()
32851     {
32852         var indicator = this.el.select('i.roo-required-indicator',true).first();
32853         
32854         if(!indicator){
32855             return false;
32856         }
32857         
32858         return indicator;
32859         
32860     },
32861     
32862     /**
32863      * Mark this field as valid
32864      */
32865     markValid : function()
32866     {
32867         if(this.indicator){
32868             this.indicator.removeClass('visible');
32869             this.indicator.addClass('invisible');
32870         }
32871         if (Roo.bootstrap.version == 3) {
32872             this.el.removeClass(this.invalidClass);
32873             this.el.addClass(this.validClass);
32874         } else {
32875             this.el.removeClass('is-invalid');
32876             this.el.addClass('is-valid');
32877         }
32878         
32879         
32880         this.fireEvent('valid', this);
32881     },
32882     
32883     /**
32884      * Mark this field as invalid
32885      * @param {String} msg The validation message
32886      */
32887     markInvalid : function(msg)
32888     {
32889         if(this.indicator){
32890             this.indicator.removeClass('invisible');
32891             this.indicator.addClass('visible');
32892         }
32893           if (Roo.bootstrap.version == 3) {
32894             this.el.removeClass(this.validClass);
32895             this.el.addClass(this.invalidClass);
32896         } else {
32897             this.el.removeClass('is-valid');
32898             this.el.addClass('is-invalid');
32899         }
32900         
32901         
32902         this.fireEvent('invalid', this, msg);
32903     }
32904     
32905    
32906 });
32907
32908 Roo.apply(Roo.bootstrap.FieldLabel, {
32909     
32910     groups: {},
32911     
32912      /**
32913     * register a FieldLabel Group
32914     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32915     */
32916     register : function(label)
32917     {
32918         if(this.groups.hasOwnProperty(label.target)){
32919             return;
32920         }
32921      
32922         this.groups[label.target] = label;
32923         
32924     },
32925     /**
32926     * fetch a FieldLabel Group based on the target
32927     * @param {string} target
32928     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32929     */
32930     get: function(target) {
32931         if (typeof(this.groups[target]) == 'undefined') {
32932             return false;
32933         }
32934         
32935         return this.groups[target] ;
32936     }
32937 });
32938
32939  
32940
32941  /*
32942  * - LGPL
32943  *
32944  * page DateSplitField.
32945  * 
32946  */
32947
32948
32949 /**
32950  * @class Roo.bootstrap.DateSplitField
32951  * @extends Roo.bootstrap.Component
32952  * Bootstrap DateSplitField class
32953  * @cfg {string} fieldLabel - the label associated
32954  * @cfg {Number} labelWidth set the width of label (0-12)
32955  * @cfg {String} labelAlign (top|left)
32956  * @cfg {Boolean} dayAllowBlank (true|false) default false
32957  * @cfg {Boolean} monthAllowBlank (true|false) default false
32958  * @cfg {Boolean} yearAllowBlank (true|false) default false
32959  * @cfg {string} dayPlaceholder 
32960  * @cfg {string} monthPlaceholder
32961  * @cfg {string} yearPlaceholder
32962  * @cfg {string} dayFormat default 'd'
32963  * @cfg {string} monthFormat default 'm'
32964  * @cfg {string} yearFormat default 'Y'
32965  * @cfg {Number} labellg set the width of label (1-12)
32966  * @cfg {Number} labelmd set the width of label (1-12)
32967  * @cfg {Number} labelsm set the width of label (1-12)
32968  * @cfg {Number} labelxs set the width of label (1-12)
32969
32970  *     
32971  * @constructor
32972  * Create a new DateSplitField
32973  * @param {Object} config The config object
32974  */
32975
32976 Roo.bootstrap.DateSplitField = function(config){
32977     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32978     
32979     this.addEvents({
32980         // raw events
32981          /**
32982          * @event years
32983          * getting the data of years
32984          * @param {Roo.bootstrap.DateSplitField} this
32985          * @param {Object} years
32986          */
32987         "years" : true,
32988         /**
32989          * @event days
32990          * getting the data of days
32991          * @param {Roo.bootstrap.DateSplitField} this
32992          * @param {Object} days
32993          */
32994         "days" : true,
32995         /**
32996          * @event invalid
32997          * Fires after the field has been marked as invalid.
32998          * @param {Roo.form.Field} this
32999          * @param {String} msg The validation message
33000          */
33001         invalid : true,
33002        /**
33003          * @event valid
33004          * Fires after the field has been validated with no errors.
33005          * @param {Roo.form.Field} this
33006          */
33007         valid : true
33008     });
33009 };
33010
33011 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33012     
33013     fieldLabel : '',
33014     labelAlign : 'top',
33015     labelWidth : 3,
33016     dayAllowBlank : false,
33017     monthAllowBlank : false,
33018     yearAllowBlank : false,
33019     dayPlaceholder : '',
33020     monthPlaceholder : '',
33021     yearPlaceholder : '',
33022     dayFormat : 'd',
33023     monthFormat : 'm',
33024     yearFormat : 'Y',
33025     isFormField : true,
33026     labellg : 0,
33027     labelmd : 0,
33028     labelsm : 0,
33029     labelxs : 0,
33030     
33031     getAutoCreate : function()
33032     {
33033         var cfg = {
33034             tag : 'div',
33035             cls : 'row roo-date-split-field-group',
33036             cn : [
33037                 {
33038                     tag : 'input',
33039                     type : 'hidden',
33040                     cls : 'form-hidden-field roo-date-split-field-group-value',
33041                     name : this.name
33042                 }
33043             ]
33044         };
33045         
33046         var labelCls = 'col-md-12';
33047         var contentCls = 'col-md-4';
33048         
33049         if(this.fieldLabel){
33050             
33051             var label = {
33052                 tag : 'div',
33053                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33054                 cn : [
33055                     {
33056                         tag : 'label',
33057                         html : this.fieldLabel
33058                     }
33059                 ]
33060             };
33061             
33062             if(this.labelAlign == 'left'){
33063             
33064                 if(this.labelWidth > 12){
33065                     label.style = "width: " + this.labelWidth + 'px';
33066                 }
33067
33068                 if(this.labelWidth < 13 && this.labelmd == 0){
33069                     this.labelmd = this.labelWidth;
33070                 }
33071
33072                 if(this.labellg > 0){
33073                     labelCls = ' col-lg-' + this.labellg;
33074                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33075                 }
33076
33077                 if(this.labelmd > 0){
33078                     labelCls = ' col-md-' + this.labelmd;
33079                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33080                 }
33081
33082                 if(this.labelsm > 0){
33083                     labelCls = ' col-sm-' + this.labelsm;
33084                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33085                 }
33086
33087                 if(this.labelxs > 0){
33088                     labelCls = ' col-xs-' + this.labelxs;
33089                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33090                 }
33091             }
33092             
33093             label.cls += ' ' + labelCls;
33094             
33095             cfg.cn.push(label);
33096         }
33097         
33098         Roo.each(['day', 'month', 'year'], function(t){
33099             cfg.cn.push({
33100                 tag : 'div',
33101                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33102             });
33103         }, this);
33104         
33105         return cfg;
33106     },
33107     
33108     inputEl: function ()
33109     {
33110         return this.el.select('.roo-date-split-field-group-value', true).first();
33111     },
33112     
33113     onRender : function(ct, position) 
33114     {
33115         var _this = this;
33116         
33117         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33118         
33119         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33120         
33121         this.dayField = new Roo.bootstrap.ComboBox({
33122             allowBlank : this.dayAllowBlank,
33123             alwaysQuery : true,
33124             displayField : 'value',
33125             editable : false,
33126             fieldLabel : '',
33127             forceSelection : true,
33128             mode : 'local',
33129             placeholder : this.dayPlaceholder,
33130             selectOnFocus : true,
33131             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33132             triggerAction : 'all',
33133             typeAhead : true,
33134             valueField : 'value',
33135             store : new Roo.data.SimpleStore({
33136                 data : (function() {    
33137                     var days = [];
33138                     _this.fireEvent('days', _this, days);
33139                     return days;
33140                 })(),
33141                 fields : [ 'value' ]
33142             }),
33143             listeners : {
33144                 select : function (_self, record, index)
33145                 {
33146                     _this.setValue(_this.getValue());
33147                 }
33148             }
33149         });
33150
33151         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33152         
33153         this.monthField = new Roo.bootstrap.MonthField({
33154             after : '<i class=\"fa fa-calendar\"></i>',
33155             allowBlank : this.monthAllowBlank,
33156             placeholder : this.monthPlaceholder,
33157             readOnly : true,
33158             listeners : {
33159                 render : function (_self)
33160                 {
33161                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33162                         e.preventDefault();
33163                         _self.focus();
33164                     });
33165                 },
33166                 select : function (_self, oldvalue, newvalue)
33167                 {
33168                     _this.setValue(_this.getValue());
33169                 }
33170             }
33171         });
33172         
33173         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33174         
33175         this.yearField = new Roo.bootstrap.ComboBox({
33176             allowBlank : this.yearAllowBlank,
33177             alwaysQuery : true,
33178             displayField : 'value',
33179             editable : false,
33180             fieldLabel : '',
33181             forceSelection : true,
33182             mode : 'local',
33183             placeholder : this.yearPlaceholder,
33184             selectOnFocus : true,
33185             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33186             triggerAction : 'all',
33187             typeAhead : true,
33188             valueField : 'value',
33189             store : new Roo.data.SimpleStore({
33190                 data : (function() {
33191                     var years = [];
33192                     _this.fireEvent('years', _this, years);
33193                     return years;
33194                 })(),
33195                 fields : [ 'value' ]
33196             }),
33197             listeners : {
33198                 select : function (_self, record, index)
33199                 {
33200                     _this.setValue(_this.getValue());
33201                 }
33202             }
33203         });
33204
33205         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33206     },
33207     
33208     setValue : function(v, format)
33209     {
33210         this.inputEl.dom.value = v;
33211         
33212         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33213         
33214         var d = Date.parseDate(v, f);
33215         
33216         if(!d){
33217             this.validate();
33218             return;
33219         }
33220         
33221         this.setDay(d.format(this.dayFormat));
33222         this.setMonth(d.format(this.monthFormat));
33223         this.setYear(d.format(this.yearFormat));
33224         
33225         this.validate();
33226         
33227         return;
33228     },
33229     
33230     setDay : function(v)
33231     {
33232         this.dayField.setValue(v);
33233         this.inputEl.dom.value = this.getValue();
33234         this.validate();
33235         return;
33236     },
33237     
33238     setMonth : function(v)
33239     {
33240         this.monthField.setValue(v, true);
33241         this.inputEl.dom.value = this.getValue();
33242         this.validate();
33243         return;
33244     },
33245     
33246     setYear : function(v)
33247     {
33248         this.yearField.setValue(v);
33249         this.inputEl.dom.value = this.getValue();
33250         this.validate();
33251         return;
33252     },
33253     
33254     getDay : function()
33255     {
33256         return this.dayField.getValue();
33257     },
33258     
33259     getMonth : function()
33260     {
33261         return this.monthField.getValue();
33262     },
33263     
33264     getYear : function()
33265     {
33266         return this.yearField.getValue();
33267     },
33268     
33269     getValue : function()
33270     {
33271         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33272         
33273         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33274         
33275         return date;
33276     },
33277     
33278     reset : function()
33279     {
33280         this.setDay('');
33281         this.setMonth('');
33282         this.setYear('');
33283         this.inputEl.dom.value = '';
33284         this.validate();
33285         return;
33286     },
33287     
33288     validate : function()
33289     {
33290         var d = this.dayField.validate();
33291         var m = this.monthField.validate();
33292         var y = this.yearField.validate();
33293         
33294         var valid = true;
33295         
33296         if(
33297                 (!this.dayAllowBlank && !d) ||
33298                 (!this.monthAllowBlank && !m) ||
33299                 (!this.yearAllowBlank && !y)
33300         ){
33301             valid = false;
33302         }
33303         
33304         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33305             return valid;
33306         }
33307         
33308         if(valid){
33309             this.markValid();
33310             return valid;
33311         }
33312         
33313         this.markInvalid();
33314         
33315         return valid;
33316     },
33317     
33318     markValid : function()
33319     {
33320         
33321         var label = this.el.select('label', true).first();
33322         var icon = this.el.select('i.fa-star', true).first();
33323
33324         if(label && icon){
33325             icon.remove();
33326         }
33327         
33328         this.fireEvent('valid', this);
33329     },
33330     
33331      /**
33332      * Mark this field as invalid
33333      * @param {String} msg The validation message
33334      */
33335     markInvalid : function(msg)
33336     {
33337         
33338         var label = this.el.select('label', true).first();
33339         var icon = this.el.select('i.fa-star', true).first();
33340
33341         if(label && !icon){
33342             this.el.select('.roo-date-split-field-label', true).createChild({
33343                 tag : 'i',
33344                 cls : 'text-danger fa fa-lg fa-star',
33345                 tooltip : 'This field is required',
33346                 style : 'margin-right:5px;'
33347             }, label, true);
33348         }
33349         
33350         this.fireEvent('invalid', this, msg);
33351     },
33352     
33353     clearInvalid : function()
33354     {
33355         var label = this.el.select('label', true).first();
33356         var icon = this.el.select('i.fa-star', true).first();
33357
33358         if(label && icon){
33359             icon.remove();
33360         }
33361         
33362         this.fireEvent('valid', this);
33363     },
33364     
33365     getName: function()
33366     {
33367         return this.name;
33368     }
33369     
33370 });
33371
33372  /**
33373  *
33374  * This is based on 
33375  * http://masonry.desandro.com
33376  *
33377  * The idea is to render all the bricks based on vertical width...
33378  *
33379  * The original code extends 'outlayer' - we might need to use that....
33380  * 
33381  */
33382
33383
33384 /**
33385  * @class Roo.bootstrap.LayoutMasonry
33386  * @extends Roo.bootstrap.Component
33387  * Bootstrap Layout Masonry class
33388  * 
33389  * @constructor
33390  * Create a new Element
33391  * @param {Object} config The config object
33392  */
33393
33394 Roo.bootstrap.LayoutMasonry = function(config){
33395     
33396     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33397     
33398     this.bricks = [];
33399     
33400     Roo.bootstrap.LayoutMasonry.register(this);
33401     
33402     this.addEvents({
33403         // raw events
33404         /**
33405          * @event layout
33406          * Fire after layout the items
33407          * @param {Roo.bootstrap.LayoutMasonry} this
33408          * @param {Roo.EventObject} e
33409          */
33410         "layout" : true
33411     });
33412     
33413 };
33414
33415 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33416     
33417     /**
33418      * @cfg {Boolean} isLayoutInstant = no animation?
33419      */   
33420     isLayoutInstant : false, // needed?
33421    
33422     /**
33423      * @cfg {Number} boxWidth  width of the columns
33424      */   
33425     boxWidth : 450,
33426     
33427       /**
33428      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33429      */   
33430     boxHeight : 0,
33431     
33432     /**
33433      * @cfg {Number} padWidth padding below box..
33434      */   
33435     padWidth : 10, 
33436     
33437     /**
33438      * @cfg {Number} gutter gutter width..
33439      */   
33440     gutter : 10,
33441     
33442      /**
33443      * @cfg {Number} maxCols maximum number of columns
33444      */   
33445     
33446     maxCols: 0,
33447     
33448     /**
33449      * @cfg {Boolean} isAutoInitial defalut true
33450      */   
33451     isAutoInitial : true, 
33452     
33453     containerWidth: 0,
33454     
33455     /**
33456      * @cfg {Boolean} isHorizontal defalut false
33457      */   
33458     isHorizontal : false, 
33459
33460     currentSize : null,
33461     
33462     tag: 'div',
33463     
33464     cls: '',
33465     
33466     bricks: null, //CompositeElement
33467     
33468     cols : 1,
33469     
33470     _isLayoutInited : false,
33471     
33472 //    isAlternative : false, // only use for vertical layout...
33473     
33474     /**
33475      * @cfg {Number} alternativePadWidth padding below box..
33476      */   
33477     alternativePadWidth : 50,
33478     
33479     selectedBrick : [],
33480     
33481     getAutoCreate : function(){
33482         
33483         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33484         
33485         var cfg = {
33486             tag: this.tag,
33487             cls: 'blog-masonary-wrapper ' + this.cls,
33488             cn : {
33489                 cls : 'mas-boxes masonary'
33490             }
33491         };
33492         
33493         return cfg;
33494     },
33495     
33496     getChildContainer: function( )
33497     {
33498         if (this.boxesEl) {
33499             return this.boxesEl;
33500         }
33501         
33502         this.boxesEl = this.el.select('.mas-boxes').first();
33503         
33504         return this.boxesEl;
33505     },
33506     
33507     
33508     initEvents : function()
33509     {
33510         var _this = this;
33511         
33512         if(this.isAutoInitial){
33513             Roo.log('hook children rendered');
33514             this.on('childrenrendered', function() {
33515                 Roo.log('children rendered');
33516                 _this.initial();
33517             } ,this);
33518         }
33519     },
33520     
33521     initial : function()
33522     {
33523         this.selectedBrick = [];
33524         
33525         this.currentSize = this.el.getBox(true);
33526         
33527         Roo.EventManager.onWindowResize(this.resize, this); 
33528
33529         if(!this.isAutoInitial){
33530             this.layout();
33531             return;
33532         }
33533         
33534         this.layout();
33535         
33536         return;
33537         //this.layout.defer(500,this);
33538         
33539     },
33540     
33541     resize : function()
33542     {
33543         var cs = this.el.getBox(true);
33544         
33545         if (
33546                 this.currentSize.width == cs.width && 
33547                 this.currentSize.x == cs.x && 
33548                 this.currentSize.height == cs.height && 
33549                 this.currentSize.y == cs.y 
33550         ) {
33551             Roo.log("no change in with or X or Y");
33552             return;
33553         }
33554         
33555         this.currentSize = cs;
33556         
33557         this.layout();
33558         
33559     },
33560     
33561     layout : function()
33562     {   
33563         this._resetLayout();
33564         
33565         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33566         
33567         this.layoutItems( isInstant );
33568       
33569         this._isLayoutInited = true;
33570         
33571         this.fireEvent('layout', this);
33572         
33573     },
33574     
33575     _resetLayout : function()
33576     {
33577         if(this.isHorizontal){
33578             this.horizontalMeasureColumns();
33579             return;
33580         }
33581         
33582         this.verticalMeasureColumns();
33583         
33584     },
33585     
33586     verticalMeasureColumns : function()
33587     {
33588         this.getContainerWidth();
33589         
33590 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33591 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33592 //            return;
33593 //        }
33594         
33595         var boxWidth = this.boxWidth + this.padWidth;
33596         
33597         if(this.containerWidth < this.boxWidth){
33598             boxWidth = this.containerWidth
33599         }
33600         
33601         var containerWidth = this.containerWidth;
33602         
33603         var cols = Math.floor(containerWidth / boxWidth);
33604         
33605         this.cols = Math.max( cols, 1 );
33606         
33607         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33608         
33609         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33610         
33611         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33612         
33613         this.colWidth = boxWidth + avail - this.padWidth;
33614         
33615         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33616         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33617     },
33618     
33619     horizontalMeasureColumns : function()
33620     {
33621         this.getContainerWidth();
33622         
33623         var boxWidth = this.boxWidth;
33624         
33625         if(this.containerWidth < boxWidth){
33626             boxWidth = this.containerWidth;
33627         }
33628         
33629         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33630         
33631         this.el.setHeight(boxWidth);
33632         
33633     },
33634     
33635     getContainerWidth : function()
33636     {
33637         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33638     },
33639     
33640     layoutItems : function( isInstant )
33641     {
33642         Roo.log(this.bricks);
33643         
33644         var items = Roo.apply([], this.bricks);
33645         
33646         if(this.isHorizontal){
33647             this._horizontalLayoutItems( items , isInstant );
33648             return;
33649         }
33650         
33651 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33652 //            this._verticalAlternativeLayoutItems( items , isInstant );
33653 //            return;
33654 //        }
33655         
33656         this._verticalLayoutItems( items , isInstant );
33657         
33658     },
33659     
33660     _verticalLayoutItems : function ( items , isInstant)
33661     {
33662         if ( !items || !items.length ) {
33663             return;
33664         }
33665         
33666         var standard = [
33667             ['xs', 'xs', 'xs', 'tall'],
33668             ['xs', 'xs', 'tall'],
33669             ['xs', 'xs', 'sm'],
33670             ['xs', 'xs', 'xs'],
33671             ['xs', 'tall'],
33672             ['xs', 'sm'],
33673             ['xs', 'xs'],
33674             ['xs'],
33675             
33676             ['sm', 'xs', 'xs'],
33677             ['sm', 'xs'],
33678             ['sm'],
33679             
33680             ['tall', 'xs', 'xs', 'xs'],
33681             ['tall', 'xs', 'xs'],
33682             ['tall', 'xs'],
33683             ['tall']
33684             
33685         ];
33686         
33687         var queue = [];
33688         
33689         var boxes = [];
33690         
33691         var box = [];
33692         
33693         Roo.each(items, function(item, k){
33694             
33695             switch (item.size) {
33696                 // these layouts take up a full box,
33697                 case 'md' :
33698                 case 'md-left' :
33699                 case 'md-right' :
33700                 case 'wide' :
33701                     
33702                     if(box.length){
33703                         boxes.push(box);
33704                         box = [];
33705                     }
33706                     
33707                     boxes.push([item]);
33708                     
33709                     break;
33710                     
33711                 case 'xs' :
33712                 case 'sm' :
33713                 case 'tall' :
33714                     
33715                     box.push(item);
33716                     
33717                     break;
33718                 default :
33719                     break;
33720                     
33721             }
33722             
33723         }, this);
33724         
33725         if(box.length){
33726             boxes.push(box);
33727             box = [];
33728         }
33729         
33730         var filterPattern = function(box, length)
33731         {
33732             if(!box.length){
33733                 return;
33734             }
33735             
33736             var match = false;
33737             
33738             var pattern = box.slice(0, length);
33739             
33740             var format = [];
33741             
33742             Roo.each(pattern, function(i){
33743                 format.push(i.size);
33744             }, this);
33745             
33746             Roo.each(standard, function(s){
33747                 
33748                 if(String(s) != String(format)){
33749                     return;
33750                 }
33751                 
33752                 match = true;
33753                 return false;
33754                 
33755             }, this);
33756             
33757             if(!match && length == 1){
33758                 return;
33759             }
33760             
33761             if(!match){
33762                 filterPattern(box, length - 1);
33763                 return;
33764             }
33765                 
33766             queue.push(pattern);
33767
33768             box = box.slice(length, box.length);
33769
33770             filterPattern(box, 4);
33771
33772             return;
33773             
33774         }
33775         
33776         Roo.each(boxes, function(box, k){
33777             
33778             if(!box.length){
33779                 return;
33780             }
33781             
33782             if(box.length == 1){
33783                 queue.push(box);
33784                 return;
33785             }
33786             
33787             filterPattern(box, 4);
33788             
33789         }, this);
33790         
33791         this._processVerticalLayoutQueue( queue, isInstant );
33792         
33793     },
33794     
33795 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33796 //    {
33797 //        if ( !items || !items.length ) {
33798 //            return;
33799 //        }
33800 //
33801 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33802 //        
33803 //    },
33804     
33805     _horizontalLayoutItems : function ( items , isInstant)
33806     {
33807         if ( !items || !items.length || items.length < 3) {
33808             return;
33809         }
33810         
33811         items.reverse();
33812         
33813         var eItems = items.slice(0, 3);
33814         
33815         items = items.slice(3, items.length);
33816         
33817         var standard = [
33818             ['xs', 'xs', 'xs', 'wide'],
33819             ['xs', 'xs', 'wide'],
33820             ['xs', 'xs', 'sm'],
33821             ['xs', 'xs', 'xs'],
33822             ['xs', 'wide'],
33823             ['xs', 'sm'],
33824             ['xs', 'xs'],
33825             ['xs'],
33826             
33827             ['sm', 'xs', 'xs'],
33828             ['sm', 'xs'],
33829             ['sm'],
33830             
33831             ['wide', 'xs', 'xs', 'xs'],
33832             ['wide', 'xs', 'xs'],
33833             ['wide', 'xs'],
33834             ['wide'],
33835             
33836             ['wide-thin']
33837         ];
33838         
33839         var queue = [];
33840         
33841         var boxes = [];
33842         
33843         var box = [];
33844         
33845         Roo.each(items, function(item, k){
33846             
33847             switch (item.size) {
33848                 case 'md' :
33849                 case 'md-left' :
33850                 case 'md-right' :
33851                 case 'tall' :
33852                     
33853                     if(box.length){
33854                         boxes.push(box);
33855                         box = [];
33856                     }
33857                     
33858                     boxes.push([item]);
33859                     
33860                     break;
33861                     
33862                 case 'xs' :
33863                 case 'sm' :
33864                 case 'wide' :
33865                 case 'wide-thin' :
33866                     
33867                     box.push(item);
33868                     
33869                     break;
33870                 default :
33871                     break;
33872                     
33873             }
33874             
33875         }, this);
33876         
33877         if(box.length){
33878             boxes.push(box);
33879             box = [];
33880         }
33881         
33882         var filterPattern = function(box, length)
33883         {
33884             if(!box.length){
33885                 return;
33886             }
33887             
33888             var match = false;
33889             
33890             var pattern = box.slice(0, length);
33891             
33892             var format = [];
33893             
33894             Roo.each(pattern, function(i){
33895                 format.push(i.size);
33896             }, this);
33897             
33898             Roo.each(standard, function(s){
33899                 
33900                 if(String(s) != String(format)){
33901                     return;
33902                 }
33903                 
33904                 match = true;
33905                 return false;
33906                 
33907             }, this);
33908             
33909             if(!match && length == 1){
33910                 return;
33911             }
33912             
33913             if(!match){
33914                 filterPattern(box, length - 1);
33915                 return;
33916             }
33917                 
33918             queue.push(pattern);
33919
33920             box = box.slice(length, box.length);
33921
33922             filterPattern(box, 4);
33923
33924             return;
33925             
33926         }
33927         
33928         Roo.each(boxes, function(box, k){
33929             
33930             if(!box.length){
33931                 return;
33932             }
33933             
33934             if(box.length == 1){
33935                 queue.push(box);
33936                 return;
33937             }
33938             
33939             filterPattern(box, 4);
33940             
33941         }, this);
33942         
33943         
33944         var prune = [];
33945         
33946         var pos = this.el.getBox(true);
33947         
33948         var minX = pos.x;
33949         
33950         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33951         
33952         var hit_end = false;
33953         
33954         Roo.each(queue, function(box){
33955             
33956             if(hit_end){
33957                 
33958                 Roo.each(box, function(b){
33959                 
33960                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33961                     b.el.hide();
33962
33963                 }, this);
33964
33965                 return;
33966             }
33967             
33968             var mx = 0;
33969             
33970             Roo.each(box, function(b){
33971                 
33972                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33973                 b.el.show();
33974
33975                 mx = Math.max(mx, b.x);
33976                 
33977             }, this);
33978             
33979             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33980             
33981             if(maxX < minX){
33982                 
33983                 Roo.each(box, function(b){
33984                 
33985                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33986                     b.el.hide();
33987                     
33988                 }, this);
33989                 
33990                 hit_end = true;
33991                 
33992                 return;
33993             }
33994             
33995             prune.push(box);
33996             
33997         }, this);
33998         
33999         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34000     },
34001     
34002     /** Sets position of item in DOM
34003     * @param {Element} item
34004     * @param {Number} x - horizontal position
34005     * @param {Number} y - vertical position
34006     * @param {Boolean} isInstant - disables transitions
34007     */
34008     _processVerticalLayoutQueue : function( queue, isInstant )
34009     {
34010         var pos = this.el.getBox(true);
34011         var x = pos.x;
34012         var y = pos.y;
34013         var maxY = [];
34014         
34015         for (var i = 0; i < this.cols; i++){
34016             maxY[i] = pos.y;
34017         }
34018         
34019         Roo.each(queue, function(box, k){
34020             
34021             var col = k % this.cols;
34022             
34023             Roo.each(box, function(b,kk){
34024                 
34025                 b.el.position('absolute');
34026                 
34027                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34028                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34029                 
34030                 if(b.size == 'md-left' || b.size == 'md-right'){
34031                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34032                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34033                 }
34034                 
34035                 b.el.setWidth(width);
34036                 b.el.setHeight(height);
34037                 // iframe?
34038                 b.el.select('iframe',true).setSize(width,height);
34039                 
34040             }, this);
34041             
34042             for (var i = 0; i < this.cols; i++){
34043                 
34044                 if(maxY[i] < maxY[col]){
34045                     col = i;
34046                     continue;
34047                 }
34048                 
34049                 col = Math.min(col, i);
34050                 
34051             }
34052             
34053             x = pos.x + col * (this.colWidth + this.padWidth);
34054             
34055             y = maxY[col];
34056             
34057             var positions = [];
34058             
34059             switch (box.length){
34060                 case 1 :
34061                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34062                     break;
34063                 case 2 :
34064                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34065                     break;
34066                 case 3 :
34067                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34068                     break;
34069                 case 4 :
34070                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34071                     break;
34072                 default :
34073                     break;
34074             }
34075             
34076             Roo.each(box, function(b,kk){
34077                 
34078                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34079                 
34080                 var sz = b.el.getSize();
34081                 
34082                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34083                 
34084             }, this);
34085             
34086         }, this);
34087         
34088         var mY = 0;
34089         
34090         for (var i = 0; i < this.cols; i++){
34091             mY = Math.max(mY, maxY[i]);
34092         }
34093         
34094         this.el.setHeight(mY - pos.y);
34095         
34096     },
34097     
34098 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34099 //    {
34100 //        var pos = this.el.getBox(true);
34101 //        var x = pos.x;
34102 //        var y = pos.y;
34103 //        var maxX = pos.right;
34104 //        
34105 //        var maxHeight = 0;
34106 //        
34107 //        Roo.each(items, function(item, k){
34108 //            
34109 //            var c = k % 2;
34110 //            
34111 //            item.el.position('absolute');
34112 //                
34113 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34114 //
34115 //            item.el.setWidth(width);
34116 //
34117 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34118 //
34119 //            item.el.setHeight(height);
34120 //            
34121 //            if(c == 0){
34122 //                item.el.setXY([x, y], isInstant ? false : true);
34123 //            } else {
34124 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34125 //            }
34126 //            
34127 //            y = y + height + this.alternativePadWidth;
34128 //            
34129 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34130 //            
34131 //        }, this);
34132 //        
34133 //        this.el.setHeight(maxHeight);
34134 //        
34135 //    },
34136     
34137     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34138     {
34139         var pos = this.el.getBox(true);
34140         
34141         var minX = pos.x;
34142         var minY = pos.y;
34143         
34144         var maxX = pos.right;
34145         
34146         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34147         
34148         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34149         
34150         Roo.each(queue, function(box, k){
34151             
34152             Roo.each(box, function(b, kk){
34153                 
34154                 b.el.position('absolute');
34155                 
34156                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34157                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34158                 
34159                 if(b.size == 'md-left' || b.size == 'md-right'){
34160                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34161                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34162                 }
34163                 
34164                 b.el.setWidth(width);
34165                 b.el.setHeight(height);
34166                 
34167             }, this);
34168             
34169             if(!box.length){
34170                 return;
34171             }
34172             
34173             var positions = [];
34174             
34175             switch (box.length){
34176                 case 1 :
34177                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34178                     break;
34179                 case 2 :
34180                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34181                     break;
34182                 case 3 :
34183                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34184                     break;
34185                 case 4 :
34186                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34187                     break;
34188                 default :
34189                     break;
34190             }
34191             
34192             Roo.each(box, function(b,kk){
34193                 
34194                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34195                 
34196                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34197                 
34198             }, this);
34199             
34200         }, this);
34201         
34202     },
34203     
34204     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34205     {
34206         Roo.each(eItems, function(b,k){
34207             
34208             b.size = (k == 0) ? 'sm' : 'xs';
34209             b.x = (k == 0) ? 2 : 1;
34210             b.y = (k == 0) ? 2 : 1;
34211             
34212             b.el.position('absolute');
34213             
34214             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34215                 
34216             b.el.setWidth(width);
34217             
34218             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34219             
34220             b.el.setHeight(height);
34221             
34222         }, this);
34223
34224         var positions = [];
34225         
34226         positions.push({
34227             x : maxX - this.unitWidth * 2 - this.gutter,
34228             y : minY
34229         });
34230         
34231         positions.push({
34232             x : maxX - this.unitWidth,
34233             y : minY + (this.unitWidth + this.gutter) * 2
34234         });
34235         
34236         positions.push({
34237             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34238             y : minY
34239         });
34240         
34241         Roo.each(eItems, function(b,k){
34242             
34243             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34244
34245         }, this);
34246         
34247     },
34248     
34249     getVerticalOneBoxColPositions : function(x, y, box)
34250     {
34251         var pos = [];
34252         
34253         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34254         
34255         if(box[0].size == 'md-left'){
34256             rand = 0;
34257         }
34258         
34259         if(box[0].size == 'md-right'){
34260             rand = 1;
34261         }
34262         
34263         pos.push({
34264             x : x + (this.unitWidth + this.gutter) * rand,
34265             y : y
34266         });
34267         
34268         return pos;
34269     },
34270     
34271     getVerticalTwoBoxColPositions : function(x, y, box)
34272     {
34273         var pos = [];
34274         
34275         if(box[0].size == 'xs'){
34276             
34277             pos.push({
34278                 x : x,
34279                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34280             });
34281
34282             pos.push({
34283                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34284                 y : y
34285             });
34286             
34287             return pos;
34288             
34289         }
34290         
34291         pos.push({
34292             x : x,
34293             y : y
34294         });
34295
34296         pos.push({
34297             x : x + (this.unitWidth + this.gutter) * 2,
34298             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34299         });
34300         
34301         return pos;
34302         
34303     },
34304     
34305     getVerticalThreeBoxColPositions : function(x, y, box)
34306     {
34307         var pos = [];
34308         
34309         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34310             
34311             pos.push({
34312                 x : x,
34313                 y : y
34314             });
34315
34316             pos.push({
34317                 x : x + (this.unitWidth + this.gutter) * 1,
34318                 y : y
34319             });
34320             
34321             pos.push({
34322                 x : x + (this.unitWidth + this.gutter) * 2,
34323                 y : y
34324             });
34325             
34326             return pos;
34327             
34328         }
34329         
34330         if(box[0].size == 'xs' && box[1].size == 'xs'){
34331             
34332             pos.push({
34333                 x : x,
34334                 y : y
34335             });
34336
34337             pos.push({
34338                 x : x,
34339                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34340             });
34341             
34342             pos.push({
34343                 x : x + (this.unitWidth + this.gutter) * 1,
34344                 y : y
34345             });
34346             
34347             return pos;
34348             
34349         }
34350         
34351         pos.push({
34352             x : x,
34353             y : y
34354         });
34355
34356         pos.push({
34357             x : x + (this.unitWidth + this.gutter) * 2,
34358             y : y
34359         });
34360
34361         pos.push({
34362             x : x + (this.unitWidth + this.gutter) * 2,
34363             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34364         });
34365             
34366         return pos;
34367         
34368     },
34369     
34370     getVerticalFourBoxColPositions : function(x, y, box)
34371     {
34372         var pos = [];
34373         
34374         if(box[0].size == 'xs'){
34375             
34376             pos.push({
34377                 x : x,
34378                 y : y
34379             });
34380
34381             pos.push({
34382                 x : x,
34383                 y : y + (this.unitHeight + this.gutter) * 1
34384             });
34385             
34386             pos.push({
34387                 x : x,
34388                 y : y + (this.unitHeight + this.gutter) * 2
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.unitHeightunitWidth + this.gutter) * 2,
34412             y : y + (this.unitHeight + this.gutter) * 1
34413         });
34414
34415         pos.push({
34416             x : x + (this.unitWidth + this.gutter) * 2,
34417             y : y + (this.unitWidth + this.gutter) * 2
34418         });
34419
34420         return pos;
34421         
34422     },
34423     
34424     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34425     {
34426         var pos = [];
34427         
34428         if(box[0].size == 'md-left'){
34429             pos.push({
34430                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34431                 y : minY
34432             });
34433             
34434             return pos;
34435         }
34436         
34437         if(box[0].size == 'md-right'){
34438             pos.push({
34439                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34440                 y : minY + (this.unitWidth + this.gutter) * 1
34441             });
34442             
34443             return pos;
34444         }
34445         
34446         var rand = Math.floor(Math.random() * (4 - box[0].y));
34447         
34448         pos.push({
34449             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34450             y : minY + (this.unitWidth + this.gutter) * rand
34451         });
34452         
34453         return pos;
34454         
34455     },
34456     
34457     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34458     {
34459         var pos = [];
34460         
34461         if(box[0].size == 'xs'){
34462             
34463             pos.push({
34464                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34465                 y : minY
34466             });
34467
34468             pos.push({
34469                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34470                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34471             });
34472             
34473             return pos;
34474             
34475         }
34476         
34477         pos.push({
34478             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34479             y : minY
34480         });
34481
34482         pos.push({
34483             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34484             y : minY + (this.unitWidth + this.gutter) * 2
34485         });
34486         
34487         return pos;
34488         
34489     },
34490     
34491     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34492     {
34493         var pos = [];
34494         
34495         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34496             
34497             pos.push({
34498                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34499                 y : minY
34500             });
34501
34502             pos.push({
34503                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34504                 y : minY + (this.unitWidth + this.gutter) * 1
34505             });
34506             
34507             pos.push({
34508                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34509                 y : minY + (this.unitWidth + this.gutter) * 2
34510             });
34511             
34512             return pos;
34513             
34514         }
34515         
34516         if(box[0].size == 'xs' && box[1].size == 'xs'){
34517             
34518             pos.push({
34519                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34520                 y : minY
34521             });
34522
34523             pos.push({
34524                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34525                 y : minY
34526             });
34527             
34528             pos.push({
34529                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34530                 y : minY + (this.unitWidth + this.gutter) * 1
34531             });
34532             
34533             return pos;
34534             
34535         }
34536         
34537         pos.push({
34538             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34539             y : minY
34540         });
34541
34542         pos.push({
34543             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34544             y : minY + (this.unitWidth + this.gutter) * 2
34545         });
34546
34547         pos.push({
34548             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34549             y : minY + (this.unitWidth + this.gutter) * 2
34550         });
34551             
34552         return pos;
34553         
34554     },
34555     
34556     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34557     {
34558         var pos = [];
34559         
34560         if(box[0].size == 'xs'){
34561             
34562             pos.push({
34563                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34564                 y : minY
34565             });
34566
34567             pos.push({
34568                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].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) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34574                 y : minY
34575             });
34576             
34577             pos.push({
34578                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].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         pos.push({
34602             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),
34603             y : minY + (this.unitWidth + this.gutter) * 2
34604         });
34605
34606         return pos;
34607         
34608     },
34609     
34610     /**
34611     * remove a Masonry Brick
34612     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34613     */
34614     removeBrick : function(brick_id)
34615     {
34616         if (!brick_id) {
34617             return;
34618         }
34619         
34620         for (var i = 0; i<this.bricks.length; i++) {
34621             if (this.bricks[i].id == brick_id) {
34622                 this.bricks.splice(i,1);
34623                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34624                 this.initial();
34625             }
34626         }
34627     },
34628     
34629     /**
34630     * adds a Masonry Brick
34631     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34632     */
34633     addBrick : function(cfg)
34634     {
34635         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34636         //this.register(cn);
34637         cn.parentId = this.id;
34638         cn.render(this.el);
34639         return cn;
34640     },
34641     
34642     /**
34643     * register a Masonry Brick
34644     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34645     */
34646     
34647     register : function(brick)
34648     {
34649         this.bricks.push(brick);
34650         brick.masonryId = this.id;
34651     },
34652     
34653     /**
34654     * clear all the Masonry Brick
34655     */
34656     clearAll : function()
34657     {
34658         this.bricks = [];
34659         //this.getChildContainer().dom.innerHTML = "";
34660         this.el.dom.innerHTML = '';
34661     },
34662     
34663     getSelected : function()
34664     {
34665         if (!this.selectedBrick) {
34666             return false;
34667         }
34668         
34669         return this.selectedBrick;
34670     }
34671 });
34672
34673 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34674     
34675     groups: {},
34676      /**
34677     * register a Masonry Layout
34678     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34679     */
34680     
34681     register : function(layout)
34682     {
34683         this.groups[layout.id] = layout;
34684     },
34685     /**
34686     * fetch a  Masonry Layout based on the masonry layout ID
34687     * @param {string} the masonry layout to add
34688     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34689     */
34690     
34691     get: function(layout_id) {
34692         if (typeof(this.groups[layout_id]) == 'undefined') {
34693             return false;
34694         }
34695         return this.groups[layout_id] ;
34696     }
34697     
34698     
34699     
34700 });
34701
34702  
34703
34704  /**
34705  *
34706  * This is based on 
34707  * http://masonry.desandro.com
34708  *
34709  * The idea is to render all the bricks based on vertical width...
34710  *
34711  * The original code extends 'outlayer' - we might need to use that....
34712  * 
34713  */
34714
34715
34716 /**
34717  * @class Roo.bootstrap.LayoutMasonryAuto
34718  * @extends Roo.bootstrap.Component
34719  * Bootstrap Layout Masonry class
34720  * 
34721  * @constructor
34722  * Create a new Element
34723  * @param {Object} config The config object
34724  */
34725
34726 Roo.bootstrap.LayoutMasonryAuto = function(config){
34727     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34728 };
34729
34730 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34731     
34732       /**
34733      * @cfg {Boolean} isFitWidth  - resize the width..
34734      */   
34735     isFitWidth : false,  // options..
34736     /**
34737      * @cfg {Boolean} isOriginLeft = left align?
34738      */   
34739     isOriginLeft : true,
34740     /**
34741      * @cfg {Boolean} isOriginTop = top align?
34742      */   
34743     isOriginTop : false,
34744     /**
34745      * @cfg {Boolean} isLayoutInstant = no animation?
34746      */   
34747     isLayoutInstant : false, // needed?
34748     /**
34749      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34750      */   
34751     isResizingContainer : true,
34752     /**
34753      * @cfg {Number} columnWidth  width of the columns 
34754      */   
34755     
34756     columnWidth : 0,
34757     
34758     /**
34759      * @cfg {Number} maxCols maximum number of columns
34760      */   
34761     
34762     maxCols: 0,
34763     /**
34764      * @cfg {Number} padHeight padding below box..
34765      */   
34766     
34767     padHeight : 10, 
34768     
34769     /**
34770      * @cfg {Boolean} isAutoInitial defalut true
34771      */   
34772     
34773     isAutoInitial : true, 
34774     
34775     // private?
34776     gutter : 0,
34777     
34778     containerWidth: 0,
34779     initialColumnWidth : 0,
34780     currentSize : null,
34781     
34782     colYs : null, // array.
34783     maxY : 0,
34784     padWidth: 10,
34785     
34786     
34787     tag: 'div',
34788     cls: '',
34789     bricks: null, //CompositeElement
34790     cols : 0, // array?
34791     // element : null, // wrapped now this.el
34792     _isLayoutInited : null, 
34793     
34794     
34795     getAutoCreate : function(){
34796         
34797         var cfg = {
34798             tag: this.tag,
34799             cls: 'blog-masonary-wrapper ' + this.cls,
34800             cn : {
34801                 cls : 'mas-boxes masonary'
34802             }
34803         };
34804         
34805         return cfg;
34806     },
34807     
34808     getChildContainer: function( )
34809     {
34810         if (this.boxesEl) {
34811             return this.boxesEl;
34812         }
34813         
34814         this.boxesEl = this.el.select('.mas-boxes').first();
34815         
34816         return this.boxesEl;
34817     },
34818     
34819     
34820     initEvents : function()
34821     {
34822         var _this = this;
34823         
34824         if(this.isAutoInitial){
34825             Roo.log('hook children rendered');
34826             this.on('childrenrendered', function() {
34827                 Roo.log('children rendered');
34828                 _this.initial();
34829             } ,this);
34830         }
34831         
34832     },
34833     
34834     initial : function()
34835     {
34836         this.reloadItems();
34837
34838         this.currentSize = this.el.getBox(true);
34839
34840         /// was window resize... - let's see if this works..
34841         Roo.EventManager.onWindowResize(this.resize, this); 
34842
34843         if(!this.isAutoInitial){
34844             this.layout();
34845             return;
34846         }
34847         
34848         this.layout.defer(500,this);
34849     },
34850     
34851     reloadItems: function()
34852     {
34853         this.bricks = this.el.select('.masonry-brick', true);
34854         
34855         this.bricks.each(function(b) {
34856             //Roo.log(b.getSize());
34857             if (!b.attr('originalwidth')) {
34858                 b.attr('originalwidth',  b.getSize().width);
34859             }
34860             
34861         });
34862         
34863         Roo.log(this.bricks.elements.length);
34864     },
34865     
34866     resize : function()
34867     {
34868         Roo.log('resize');
34869         var cs = this.el.getBox(true);
34870         
34871         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34872             Roo.log("no change in with or X");
34873             return;
34874         }
34875         this.currentSize = cs;
34876         this.layout();
34877     },
34878     
34879     layout : function()
34880     {
34881          Roo.log('layout');
34882         this._resetLayout();
34883         //this._manageStamps();
34884       
34885         // don't animate first layout
34886         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34887         this.layoutItems( isInstant );
34888       
34889         // flag for initalized
34890         this._isLayoutInited = true;
34891     },
34892     
34893     layoutItems : function( isInstant )
34894     {
34895         //var items = this._getItemsForLayout( this.items );
34896         // original code supports filtering layout items.. we just ignore it..
34897         
34898         this._layoutItems( this.bricks , isInstant );
34899       
34900         this._postLayout();
34901     },
34902     _layoutItems : function ( items , isInstant)
34903     {
34904        //this.fireEvent( 'layout', this, items );
34905     
34906
34907         if ( !items || !items.elements.length ) {
34908           // no items, emit event with empty array
34909             return;
34910         }
34911
34912         var queue = [];
34913         items.each(function(item) {
34914             Roo.log("layout item");
34915             Roo.log(item);
34916             // get x/y object from method
34917             var position = this._getItemLayoutPosition( item );
34918             // enqueue
34919             position.item = item;
34920             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34921             queue.push( position );
34922         }, this);
34923       
34924         this._processLayoutQueue( queue );
34925     },
34926     /** Sets position of item in DOM
34927     * @param {Element} item
34928     * @param {Number} x - horizontal position
34929     * @param {Number} y - vertical position
34930     * @param {Boolean} isInstant - disables transitions
34931     */
34932     _processLayoutQueue : function( queue )
34933     {
34934         for ( var i=0, len = queue.length; i < len; i++ ) {
34935             var obj = queue[i];
34936             obj.item.position('absolute');
34937             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34938         }
34939     },
34940       
34941     
34942     /**
34943     * Any logic you want to do after each layout,
34944     * i.e. size the container
34945     */
34946     _postLayout : function()
34947     {
34948         this.resizeContainer();
34949     },
34950     
34951     resizeContainer : function()
34952     {
34953         if ( !this.isResizingContainer ) {
34954             return;
34955         }
34956         var size = this._getContainerSize();
34957         if ( size ) {
34958             this.el.setSize(size.width,size.height);
34959             this.boxesEl.setSize(size.width,size.height);
34960         }
34961     },
34962     
34963     
34964     
34965     _resetLayout : function()
34966     {
34967         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34968         this.colWidth = this.el.getWidth();
34969         //this.gutter = this.el.getWidth(); 
34970         
34971         this.measureColumns();
34972
34973         // reset column Y
34974         var i = this.cols;
34975         this.colYs = [];
34976         while (i--) {
34977             this.colYs.push( 0 );
34978         }
34979     
34980         this.maxY = 0;
34981     },
34982
34983     measureColumns : function()
34984     {
34985         this.getContainerWidth();
34986       // if columnWidth is 0, default to outerWidth of first item
34987         if ( !this.columnWidth ) {
34988             var firstItem = this.bricks.first();
34989             Roo.log(firstItem);
34990             this.columnWidth  = this.containerWidth;
34991             if (firstItem && firstItem.attr('originalwidth') ) {
34992                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34993             }
34994             // columnWidth fall back to item of first element
34995             Roo.log("set column width?");
34996                         this.initialColumnWidth = this.columnWidth  ;
34997
34998             // if first elem has no width, default to size of container
34999             
35000         }
35001         
35002         
35003         if (this.initialColumnWidth) {
35004             this.columnWidth = this.initialColumnWidth;
35005         }
35006         
35007         
35008             
35009         // column width is fixed at the top - however if container width get's smaller we should
35010         // reduce it...
35011         
35012         // this bit calcs how man columns..
35013             
35014         var columnWidth = this.columnWidth += this.gutter;
35015       
35016         // calculate columns
35017         var containerWidth = this.containerWidth + this.gutter;
35018         
35019         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35020         // fix rounding errors, typically with gutters
35021         var excess = columnWidth - containerWidth % columnWidth;
35022         
35023         
35024         // if overshoot is less than a pixel, round up, otherwise floor it
35025         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35026         cols = Math[ mathMethod ]( cols );
35027         this.cols = Math.max( cols, 1 );
35028         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35029         
35030          // padding positioning..
35031         var totalColWidth = this.cols * this.columnWidth;
35032         var padavail = this.containerWidth - totalColWidth;
35033         // so for 2 columns - we need 3 'pads'
35034         
35035         var padNeeded = (1+this.cols) * this.padWidth;
35036         
35037         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35038         
35039         this.columnWidth += padExtra
35040         //this.padWidth = Math.floor(padavail /  ( this.cols));
35041         
35042         // adjust colum width so that padding is fixed??
35043         
35044         // we have 3 columns ... total = width * 3
35045         // we have X left over... that should be used by 
35046         
35047         //if (this.expandC) {
35048             
35049         //}
35050         
35051         
35052         
35053     },
35054     
35055     getContainerWidth : function()
35056     {
35057        /* // container is parent if fit width
35058         var container = this.isFitWidth ? this.element.parentNode : this.element;
35059         // check that this.size and size are there
35060         // IE8 triggers resize on body size change, so they might not be
35061         
35062         var size = getSize( container );  //FIXME
35063         this.containerWidth = size && size.innerWidth; //FIXME
35064         */
35065          
35066         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35067         
35068     },
35069     
35070     _getItemLayoutPosition : function( item )  // what is item?
35071     {
35072         // we resize the item to our columnWidth..
35073       
35074         item.setWidth(this.columnWidth);
35075         item.autoBoxAdjust  = false;
35076         
35077         var sz = item.getSize();
35078  
35079         // how many columns does this brick span
35080         var remainder = this.containerWidth % this.columnWidth;
35081         
35082         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35083         // round if off by 1 pixel, otherwise use ceil
35084         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35085         colSpan = Math.min( colSpan, this.cols );
35086         
35087         // normally this should be '1' as we dont' currently allow multi width columns..
35088         
35089         var colGroup = this._getColGroup( colSpan );
35090         // get the minimum Y value from the columns
35091         var minimumY = Math.min.apply( Math, colGroup );
35092         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35093         
35094         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35095          
35096         // position the brick
35097         var position = {
35098             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35099             y: this.currentSize.y + minimumY + this.padHeight
35100         };
35101         
35102         Roo.log(position);
35103         // apply setHeight to necessary columns
35104         var setHeight = minimumY + sz.height + this.padHeight;
35105         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35106         
35107         var setSpan = this.cols + 1 - colGroup.length;
35108         for ( var i = 0; i < setSpan; i++ ) {
35109           this.colYs[ shortColIndex + i ] = setHeight ;
35110         }
35111       
35112         return position;
35113     },
35114     
35115     /**
35116      * @param {Number} colSpan - number of columns the element spans
35117      * @returns {Array} colGroup
35118      */
35119     _getColGroup : function( colSpan )
35120     {
35121         if ( colSpan < 2 ) {
35122           // if brick spans only one column, use all the column Ys
35123           return this.colYs;
35124         }
35125       
35126         var colGroup = [];
35127         // how many different places could this brick fit horizontally
35128         var groupCount = this.cols + 1 - colSpan;
35129         // for each group potential horizontal position
35130         for ( var i = 0; i < groupCount; i++ ) {
35131           // make an array of colY values for that one group
35132           var groupColYs = this.colYs.slice( i, i + colSpan );
35133           // and get the max value of the array
35134           colGroup[i] = Math.max.apply( Math, groupColYs );
35135         }
35136         return colGroup;
35137     },
35138     /*
35139     _manageStamp : function( stamp )
35140     {
35141         var stampSize =  stamp.getSize();
35142         var offset = stamp.getBox();
35143         // get the columns that this stamp affects
35144         var firstX = this.isOriginLeft ? offset.x : offset.right;
35145         var lastX = firstX + stampSize.width;
35146         var firstCol = Math.floor( firstX / this.columnWidth );
35147         firstCol = Math.max( 0, firstCol );
35148         
35149         var lastCol = Math.floor( lastX / this.columnWidth );
35150         // lastCol should not go over if multiple of columnWidth #425
35151         lastCol -= lastX % this.columnWidth ? 0 : 1;
35152         lastCol = Math.min( this.cols - 1, lastCol );
35153         
35154         // set colYs to bottom of the stamp
35155         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35156             stampSize.height;
35157             
35158         for ( var i = firstCol; i <= lastCol; i++ ) {
35159           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35160         }
35161     },
35162     */
35163     
35164     _getContainerSize : function()
35165     {
35166         this.maxY = Math.max.apply( Math, this.colYs );
35167         var size = {
35168             height: this.maxY
35169         };
35170       
35171         if ( this.isFitWidth ) {
35172             size.width = this._getContainerFitWidth();
35173         }
35174       
35175         return size;
35176     },
35177     
35178     _getContainerFitWidth : function()
35179     {
35180         var unusedCols = 0;
35181         // count unused columns
35182         var i = this.cols;
35183         while ( --i ) {
35184           if ( this.colYs[i] !== 0 ) {
35185             break;
35186           }
35187           unusedCols++;
35188         }
35189         // fit container to columns that have been used
35190         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35191     },
35192     
35193     needsResizeLayout : function()
35194     {
35195         var previousWidth = this.containerWidth;
35196         this.getContainerWidth();
35197         return previousWidth !== this.containerWidth;
35198     }
35199  
35200 });
35201
35202  
35203
35204  /*
35205  * - LGPL
35206  *
35207  * element
35208  * 
35209  */
35210
35211 /**
35212  * @class Roo.bootstrap.MasonryBrick
35213  * @extends Roo.bootstrap.Component
35214  * Bootstrap MasonryBrick class
35215  * 
35216  * @constructor
35217  * Create a new MasonryBrick
35218  * @param {Object} config The config object
35219  */
35220
35221 Roo.bootstrap.MasonryBrick = function(config){
35222     
35223     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35224     
35225     Roo.bootstrap.MasonryBrick.register(this);
35226     
35227     this.addEvents({
35228         // raw events
35229         /**
35230          * @event click
35231          * When a MasonryBrick is clcik
35232          * @param {Roo.bootstrap.MasonryBrick} this
35233          * @param {Roo.EventObject} e
35234          */
35235         "click" : true
35236     });
35237 };
35238
35239 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35240     
35241     /**
35242      * @cfg {String} title
35243      */   
35244     title : '',
35245     /**
35246      * @cfg {String} html
35247      */   
35248     html : '',
35249     /**
35250      * @cfg {String} bgimage
35251      */   
35252     bgimage : '',
35253     /**
35254      * @cfg {String} videourl
35255      */   
35256     videourl : '',
35257     /**
35258      * @cfg {String} cls
35259      */   
35260     cls : '',
35261     /**
35262      * @cfg {String} href
35263      */   
35264     href : '',
35265     /**
35266      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35267      */   
35268     size : 'xs',
35269     
35270     /**
35271      * @cfg {String} placetitle (center|bottom)
35272      */   
35273     placetitle : '',
35274     
35275     /**
35276      * @cfg {Boolean} isFitContainer defalut true
35277      */   
35278     isFitContainer : true, 
35279     
35280     /**
35281      * @cfg {Boolean} preventDefault defalut false
35282      */   
35283     preventDefault : false, 
35284     
35285     /**
35286      * @cfg {Boolean} inverse defalut false
35287      */   
35288     maskInverse : false, 
35289     
35290     getAutoCreate : function()
35291     {
35292         if(!this.isFitContainer){
35293             return this.getSplitAutoCreate();
35294         }
35295         
35296         var cls = 'masonry-brick masonry-brick-full';
35297         
35298         if(this.href.length){
35299             cls += ' masonry-brick-link';
35300         }
35301         
35302         if(this.bgimage.length){
35303             cls += ' masonry-brick-image';
35304         }
35305         
35306         if(this.maskInverse){
35307             cls += ' mask-inverse';
35308         }
35309         
35310         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35311             cls += ' enable-mask';
35312         }
35313         
35314         if(this.size){
35315             cls += ' masonry-' + this.size + '-brick';
35316         }
35317         
35318         if(this.placetitle.length){
35319             
35320             switch (this.placetitle) {
35321                 case 'center' :
35322                     cls += ' masonry-center-title';
35323                     break;
35324                 case 'bottom' :
35325                     cls += ' masonry-bottom-title';
35326                     break;
35327                 default:
35328                     break;
35329             }
35330             
35331         } else {
35332             if(!this.html.length && !this.bgimage.length){
35333                 cls += ' masonry-center-title';
35334             }
35335
35336             if(!this.html.length && this.bgimage.length){
35337                 cls += ' masonry-bottom-title';
35338             }
35339         }
35340         
35341         if(this.cls){
35342             cls += ' ' + this.cls;
35343         }
35344         
35345         var cfg = {
35346             tag: (this.href.length) ? 'a' : 'div',
35347             cls: cls,
35348             cn: [
35349                 {
35350                     tag: 'div',
35351                     cls: 'masonry-brick-mask'
35352                 },
35353                 {
35354                     tag: 'div',
35355                     cls: 'masonry-brick-paragraph',
35356                     cn: []
35357                 }
35358             ]
35359         };
35360         
35361         if(this.href.length){
35362             cfg.href = this.href;
35363         }
35364         
35365         var cn = cfg.cn[1].cn;
35366         
35367         if(this.title.length){
35368             cn.push({
35369                 tag: 'h4',
35370                 cls: 'masonry-brick-title',
35371                 html: this.title
35372             });
35373         }
35374         
35375         if(this.html.length){
35376             cn.push({
35377                 tag: 'p',
35378                 cls: 'masonry-brick-text',
35379                 html: this.html
35380             });
35381         }
35382         
35383         if (!this.title.length && !this.html.length) {
35384             cfg.cn[1].cls += ' hide';
35385         }
35386         
35387         if(this.bgimage.length){
35388             cfg.cn.push({
35389                 tag: 'img',
35390                 cls: 'masonry-brick-image-view',
35391                 src: this.bgimage
35392             });
35393         }
35394         
35395         if(this.videourl.length){
35396             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35397             // youtube support only?
35398             cfg.cn.push({
35399                 tag: 'iframe',
35400                 cls: 'masonry-brick-image-view',
35401                 src: vurl,
35402                 frameborder : 0,
35403                 allowfullscreen : true
35404             });
35405         }
35406         
35407         return cfg;
35408         
35409     },
35410     
35411     getSplitAutoCreate : function()
35412     {
35413         var cls = 'masonry-brick masonry-brick-split';
35414         
35415         if(this.href.length){
35416             cls += ' masonry-brick-link';
35417         }
35418         
35419         if(this.bgimage.length){
35420             cls += ' masonry-brick-image';
35421         }
35422         
35423         if(this.size){
35424             cls += ' masonry-' + this.size + '-brick';
35425         }
35426         
35427         switch (this.placetitle) {
35428             case 'center' :
35429                 cls += ' masonry-center-title';
35430                 break;
35431             case 'bottom' :
35432                 cls += ' masonry-bottom-title';
35433                 break;
35434             default:
35435                 if(!this.bgimage.length){
35436                     cls += ' masonry-center-title';
35437                 }
35438
35439                 if(this.bgimage.length){
35440                     cls += ' masonry-bottom-title';
35441                 }
35442                 break;
35443         }
35444         
35445         if(this.cls){
35446             cls += ' ' + this.cls;
35447         }
35448         
35449         var cfg = {
35450             tag: (this.href.length) ? 'a' : 'div',
35451             cls: cls,
35452             cn: [
35453                 {
35454                     tag: 'div',
35455                     cls: 'masonry-brick-split-head',
35456                     cn: [
35457                         {
35458                             tag: 'div',
35459                             cls: 'masonry-brick-paragraph',
35460                             cn: []
35461                         }
35462                     ]
35463                 },
35464                 {
35465                     tag: 'div',
35466                     cls: 'masonry-brick-split-body',
35467                     cn: []
35468                 }
35469             ]
35470         };
35471         
35472         if(this.href.length){
35473             cfg.href = this.href;
35474         }
35475         
35476         if(this.title.length){
35477             cfg.cn[0].cn[0].cn.push({
35478                 tag: 'h4',
35479                 cls: 'masonry-brick-title',
35480                 html: this.title
35481             });
35482         }
35483         
35484         if(this.html.length){
35485             cfg.cn[1].cn.push({
35486                 tag: 'p',
35487                 cls: 'masonry-brick-text',
35488                 html: this.html
35489             });
35490         }
35491
35492         if(this.bgimage.length){
35493             cfg.cn[0].cn.push({
35494                 tag: 'img',
35495                 cls: 'masonry-brick-image-view',
35496                 src: this.bgimage
35497             });
35498         }
35499         
35500         if(this.videourl.length){
35501             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35502             // youtube support only?
35503             cfg.cn[0].cn.cn.push({
35504                 tag: 'iframe',
35505                 cls: 'masonry-brick-image-view',
35506                 src: vurl,
35507                 frameborder : 0,
35508                 allowfullscreen : true
35509             });
35510         }
35511         
35512         return cfg;
35513     },
35514     
35515     initEvents: function() 
35516     {
35517         switch (this.size) {
35518             case 'xs' :
35519                 this.x = 1;
35520                 this.y = 1;
35521                 break;
35522             case 'sm' :
35523                 this.x = 2;
35524                 this.y = 2;
35525                 break;
35526             case 'md' :
35527             case 'md-left' :
35528             case 'md-right' :
35529                 this.x = 3;
35530                 this.y = 3;
35531                 break;
35532             case 'tall' :
35533                 this.x = 2;
35534                 this.y = 3;
35535                 break;
35536             case 'wide' :
35537                 this.x = 3;
35538                 this.y = 2;
35539                 break;
35540             case 'wide-thin' :
35541                 this.x = 3;
35542                 this.y = 1;
35543                 break;
35544                         
35545             default :
35546                 break;
35547         }
35548         
35549         if(Roo.isTouch){
35550             this.el.on('touchstart', this.onTouchStart, this);
35551             this.el.on('touchmove', this.onTouchMove, this);
35552             this.el.on('touchend', this.onTouchEnd, this);
35553             this.el.on('contextmenu', this.onContextMenu, this);
35554         } else {
35555             this.el.on('mouseenter'  ,this.enter, this);
35556             this.el.on('mouseleave', this.leave, this);
35557             this.el.on('click', this.onClick, this);
35558         }
35559         
35560         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35561             this.parent().bricks.push(this);   
35562         }
35563         
35564     },
35565     
35566     onClick: function(e, el)
35567     {
35568         var time = this.endTimer - this.startTimer;
35569         // Roo.log(e.preventDefault());
35570         if(Roo.isTouch){
35571             if(time > 1000){
35572                 e.preventDefault();
35573                 return;
35574             }
35575         }
35576         
35577         if(!this.preventDefault){
35578             return;
35579         }
35580         
35581         e.preventDefault();
35582         
35583         if (this.activeClass != '') {
35584             this.selectBrick();
35585         }
35586         
35587         this.fireEvent('click', this, e);
35588     },
35589     
35590     enter: function(e, el)
35591     {
35592         e.preventDefault();
35593         
35594         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35595             return;
35596         }
35597         
35598         if(this.bgimage.length && this.html.length){
35599             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35600         }
35601     },
35602     
35603     leave: function(e, el)
35604     {
35605         e.preventDefault();
35606         
35607         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35608             return;
35609         }
35610         
35611         if(this.bgimage.length && this.html.length){
35612             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35613         }
35614     },
35615     
35616     onTouchStart: function(e, el)
35617     {
35618 //        e.preventDefault();
35619         
35620         this.touchmoved = false;
35621         
35622         if(!this.isFitContainer){
35623             return;
35624         }
35625         
35626         if(!this.bgimage.length || !this.html.length){
35627             return;
35628         }
35629         
35630         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35631         
35632         this.timer = new Date().getTime();
35633         
35634     },
35635     
35636     onTouchMove: function(e, el)
35637     {
35638         this.touchmoved = true;
35639     },
35640     
35641     onContextMenu : function(e,el)
35642     {
35643         e.preventDefault();
35644         e.stopPropagation();
35645         return false;
35646     },
35647     
35648     onTouchEnd: function(e, el)
35649     {
35650 //        e.preventDefault();
35651         
35652         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35653         
35654             this.leave(e,el);
35655             
35656             return;
35657         }
35658         
35659         if(!this.bgimage.length || !this.html.length){
35660             
35661             if(this.href.length){
35662                 window.location.href = this.href;
35663             }
35664             
35665             return;
35666         }
35667         
35668         if(!this.isFitContainer){
35669             return;
35670         }
35671         
35672         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35673         
35674         window.location.href = this.href;
35675     },
35676     
35677     //selection on single brick only
35678     selectBrick : function() {
35679         
35680         if (!this.parentId) {
35681             return;
35682         }
35683         
35684         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35685         var index = m.selectedBrick.indexOf(this.id);
35686         
35687         if ( index > -1) {
35688             m.selectedBrick.splice(index,1);
35689             this.el.removeClass(this.activeClass);
35690             return;
35691         }
35692         
35693         for(var i = 0; i < m.selectedBrick.length; i++) {
35694             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35695             b.el.removeClass(b.activeClass);
35696         }
35697         
35698         m.selectedBrick = [];
35699         
35700         m.selectedBrick.push(this.id);
35701         this.el.addClass(this.activeClass);
35702         return;
35703     },
35704     
35705     isSelected : function(){
35706         return this.el.hasClass(this.activeClass);
35707         
35708     }
35709 });
35710
35711 Roo.apply(Roo.bootstrap.MasonryBrick, {
35712     
35713     //groups: {},
35714     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35715      /**
35716     * register a Masonry Brick
35717     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35718     */
35719     
35720     register : function(brick)
35721     {
35722         //this.groups[brick.id] = brick;
35723         this.groups.add(brick.id, brick);
35724     },
35725     /**
35726     * fetch a  masonry brick based on the masonry brick ID
35727     * @param {string} the masonry brick to add
35728     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35729     */
35730     
35731     get: function(brick_id) 
35732     {
35733         // if (typeof(this.groups[brick_id]) == 'undefined') {
35734         //     return false;
35735         // }
35736         // return this.groups[brick_id] ;
35737         
35738         if(this.groups.key(brick_id)) {
35739             return this.groups.key(brick_id);
35740         }
35741         
35742         return false;
35743     }
35744     
35745     
35746     
35747 });
35748
35749  /*
35750  * - LGPL
35751  *
35752  * element
35753  * 
35754  */
35755
35756 /**
35757  * @class Roo.bootstrap.Brick
35758  * @extends Roo.bootstrap.Component
35759  * Bootstrap Brick class
35760  * 
35761  * @constructor
35762  * Create a new Brick
35763  * @param {Object} config The config object
35764  */
35765
35766 Roo.bootstrap.Brick = function(config){
35767     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35768     
35769     this.addEvents({
35770         // raw events
35771         /**
35772          * @event click
35773          * When a Brick is click
35774          * @param {Roo.bootstrap.Brick} this
35775          * @param {Roo.EventObject} e
35776          */
35777         "click" : true
35778     });
35779 };
35780
35781 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35782     
35783     /**
35784      * @cfg {String} title
35785      */   
35786     title : '',
35787     /**
35788      * @cfg {String} html
35789      */   
35790     html : '',
35791     /**
35792      * @cfg {String} bgimage
35793      */   
35794     bgimage : '',
35795     /**
35796      * @cfg {String} cls
35797      */   
35798     cls : '',
35799     /**
35800      * @cfg {String} href
35801      */   
35802     href : '',
35803     /**
35804      * @cfg {String} video
35805      */   
35806     video : '',
35807     /**
35808      * @cfg {Boolean} square
35809      */   
35810     square : true,
35811     
35812     getAutoCreate : function()
35813     {
35814         var cls = 'roo-brick';
35815         
35816         if(this.href.length){
35817             cls += ' roo-brick-link';
35818         }
35819         
35820         if(this.bgimage.length){
35821             cls += ' roo-brick-image';
35822         }
35823         
35824         if(!this.html.length && !this.bgimage.length){
35825             cls += ' roo-brick-center-title';
35826         }
35827         
35828         if(!this.html.length && this.bgimage.length){
35829             cls += ' roo-brick-bottom-title';
35830         }
35831         
35832         if(this.cls){
35833             cls += ' ' + this.cls;
35834         }
35835         
35836         var cfg = {
35837             tag: (this.href.length) ? 'a' : 'div',
35838             cls: cls,
35839             cn: [
35840                 {
35841                     tag: 'div',
35842                     cls: 'roo-brick-paragraph',
35843                     cn: []
35844                 }
35845             ]
35846         };
35847         
35848         if(this.href.length){
35849             cfg.href = this.href;
35850         }
35851         
35852         var cn = cfg.cn[0].cn;
35853         
35854         if(this.title.length){
35855             cn.push({
35856                 tag: 'h4',
35857                 cls: 'roo-brick-title',
35858                 html: this.title
35859             });
35860         }
35861         
35862         if(this.html.length){
35863             cn.push({
35864                 tag: 'p',
35865                 cls: 'roo-brick-text',
35866                 html: this.html
35867             });
35868         } else {
35869             cn.cls += ' hide';
35870         }
35871         
35872         if(this.bgimage.length){
35873             cfg.cn.push({
35874                 tag: 'img',
35875                 cls: 'roo-brick-image-view',
35876                 src: this.bgimage
35877             });
35878         }
35879         
35880         return cfg;
35881     },
35882     
35883     initEvents: function() 
35884     {
35885         if(this.title.length || this.html.length){
35886             this.el.on('mouseenter'  ,this.enter, this);
35887             this.el.on('mouseleave', this.leave, this);
35888         }
35889         
35890         Roo.EventManager.onWindowResize(this.resize, this); 
35891         
35892         if(this.bgimage.length){
35893             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35894             this.imageEl.on('load', this.onImageLoad, this);
35895             return;
35896         }
35897         
35898         this.resize();
35899     },
35900     
35901     onImageLoad : function()
35902     {
35903         this.resize();
35904     },
35905     
35906     resize : function()
35907     {
35908         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35909         
35910         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35911         
35912         if(this.bgimage.length){
35913             var image = this.el.select('.roo-brick-image-view', true).first();
35914             
35915             image.setWidth(paragraph.getWidth());
35916             
35917             if(this.square){
35918                 image.setHeight(paragraph.getWidth());
35919             }
35920             
35921             this.el.setHeight(image.getHeight());
35922             paragraph.setHeight(image.getHeight());
35923             
35924         }
35925         
35926     },
35927     
35928     enter: function(e, el)
35929     {
35930         e.preventDefault();
35931         
35932         if(this.bgimage.length){
35933             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35934             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35935         }
35936     },
35937     
35938     leave: function(e, el)
35939     {
35940         e.preventDefault();
35941         
35942         if(this.bgimage.length){
35943             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35944             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35945         }
35946     }
35947     
35948 });
35949
35950  
35951
35952  /*
35953  * - LGPL
35954  *
35955  * Number field 
35956  */
35957
35958 /**
35959  * @class Roo.bootstrap.NumberField
35960  * @extends Roo.bootstrap.Input
35961  * Bootstrap NumberField class
35962  * 
35963  * 
35964  * 
35965  * 
35966  * @constructor
35967  * Create a new NumberField
35968  * @param {Object} config The config object
35969  */
35970
35971 Roo.bootstrap.NumberField = function(config){
35972     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35973 };
35974
35975 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35976     
35977     /**
35978      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35979      */
35980     allowDecimals : true,
35981     /**
35982      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35983      */
35984     decimalSeparator : ".",
35985     /**
35986      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35987      */
35988     decimalPrecision : 2,
35989     /**
35990      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35991      */
35992     allowNegative : true,
35993     
35994     /**
35995      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35996      */
35997     allowZero: true,
35998     /**
35999      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36000      */
36001     minValue : Number.NEGATIVE_INFINITY,
36002     /**
36003      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36004      */
36005     maxValue : Number.MAX_VALUE,
36006     /**
36007      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36008      */
36009     minText : "The minimum value for this field is {0}",
36010     /**
36011      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36012      */
36013     maxText : "The maximum value for this field is {0}",
36014     /**
36015      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36016      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36017      */
36018     nanText : "{0} is not a valid number",
36019     /**
36020      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36021      */
36022     thousandsDelimiter : false,
36023     /**
36024      * @cfg {String} valueAlign alignment of value
36025      */
36026     valueAlign : "left",
36027
36028     getAutoCreate : function()
36029     {
36030         var hiddenInput = {
36031             tag: 'input',
36032             type: 'hidden',
36033             id: Roo.id(),
36034             cls: 'hidden-number-input'
36035         };
36036         
36037         if (this.name) {
36038             hiddenInput.name = this.name;
36039         }
36040         
36041         this.name = '';
36042         
36043         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36044         
36045         this.name = hiddenInput.name;
36046         
36047         if(cfg.cn.length > 0) {
36048             cfg.cn.push(hiddenInput);
36049         }
36050         
36051         return cfg;
36052     },
36053
36054     // private
36055     initEvents : function()
36056     {   
36057         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36058         
36059         var allowed = "0123456789";
36060         
36061         if(this.allowDecimals){
36062             allowed += this.decimalSeparator;
36063         }
36064         
36065         if(this.allowNegative){
36066             allowed += "-";
36067         }
36068         
36069         if(this.thousandsDelimiter) {
36070             allowed += ",";
36071         }
36072         
36073         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36074         
36075         var keyPress = function(e){
36076             
36077             var k = e.getKey();
36078             
36079             var c = e.getCharCode();
36080             
36081             if(
36082                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36083                     allowed.indexOf(String.fromCharCode(c)) === -1
36084             ){
36085                 e.stopEvent();
36086                 return;
36087             }
36088             
36089             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36090                 return;
36091             }
36092             
36093             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36094                 e.stopEvent();
36095             }
36096         };
36097         
36098         this.el.on("keypress", keyPress, this);
36099     },
36100     
36101     validateValue : function(value)
36102     {
36103         
36104         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36105             return false;
36106         }
36107         
36108         var num = this.parseValue(value);
36109         
36110         if(isNaN(num)){
36111             this.markInvalid(String.format(this.nanText, value));
36112             return false;
36113         }
36114         
36115         if(num < this.minValue){
36116             this.markInvalid(String.format(this.minText, this.minValue));
36117             return false;
36118         }
36119         
36120         if(num > this.maxValue){
36121             this.markInvalid(String.format(this.maxText, this.maxValue));
36122             return false;
36123         }
36124         
36125         return true;
36126     },
36127
36128     getValue : function()
36129     {
36130         var v = this.hiddenEl().getValue();
36131         
36132         return this.fixPrecision(this.parseValue(v));
36133     },
36134
36135     parseValue : function(value)
36136     {
36137         if(this.thousandsDelimiter) {
36138             value += "";
36139             r = new RegExp(",", "g");
36140             value = value.replace(r, "");
36141         }
36142         
36143         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36144         return isNaN(value) ? '' : value;
36145     },
36146
36147     fixPrecision : function(value)
36148     {
36149         if(this.thousandsDelimiter) {
36150             value += "";
36151             r = new RegExp(",", "g");
36152             value = value.replace(r, "");
36153         }
36154         
36155         var nan = isNaN(value);
36156         
36157         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36158             return nan ? '' : value;
36159         }
36160         return parseFloat(value).toFixed(this.decimalPrecision);
36161     },
36162
36163     setValue : function(v)
36164     {
36165         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36166         
36167         this.value = v;
36168         
36169         if(this.rendered){
36170             
36171             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36172             
36173             this.inputEl().dom.value = (v == '') ? '' :
36174                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36175             
36176             if(!this.allowZero && v === '0') {
36177                 this.hiddenEl().dom.value = '';
36178                 this.inputEl().dom.value = '';
36179             }
36180             
36181             this.validate();
36182         }
36183     },
36184
36185     decimalPrecisionFcn : function(v)
36186     {
36187         return Math.floor(v);
36188     },
36189
36190     beforeBlur : function()
36191     {
36192         var v = this.parseValue(this.getRawValue());
36193         
36194         if(v || v === 0 || v === ''){
36195             this.setValue(v);
36196         }
36197     },
36198     
36199     hiddenEl : function()
36200     {
36201         return this.el.select('input.hidden-number-input',true).first();
36202     }
36203     
36204 });
36205
36206  
36207
36208 /*
36209 * Licence: LGPL
36210 */
36211
36212 /**
36213  * @class Roo.bootstrap.DocumentSlider
36214  * @extends Roo.bootstrap.Component
36215  * Bootstrap DocumentSlider class
36216  * 
36217  * @constructor
36218  * Create a new DocumentViewer
36219  * @param {Object} config The config object
36220  */
36221
36222 Roo.bootstrap.DocumentSlider = function(config){
36223     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36224     
36225     this.files = [];
36226     
36227     this.addEvents({
36228         /**
36229          * @event initial
36230          * Fire after initEvent
36231          * @param {Roo.bootstrap.DocumentSlider} this
36232          */
36233         "initial" : true,
36234         /**
36235          * @event update
36236          * Fire after update
36237          * @param {Roo.bootstrap.DocumentSlider} this
36238          */
36239         "update" : true,
36240         /**
36241          * @event click
36242          * Fire after click
36243          * @param {Roo.bootstrap.DocumentSlider} this
36244          */
36245         "click" : true
36246     });
36247 };
36248
36249 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36250     
36251     files : false,
36252     
36253     indicator : 0,
36254     
36255     getAutoCreate : function()
36256     {
36257         var cfg = {
36258             tag : 'div',
36259             cls : 'roo-document-slider',
36260             cn : [
36261                 {
36262                     tag : 'div',
36263                     cls : 'roo-document-slider-header',
36264                     cn : [
36265                         {
36266                             tag : 'div',
36267                             cls : 'roo-document-slider-header-title'
36268                         }
36269                     ]
36270                 },
36271                 {
36272                     tag : 'div',
36273                     cls : 'roo-document-slider-body',
36274                     cn : [
36275                         {
36276                             tag : 'div',
36277                             cls : 'roo-document-slider-prev',
36278                             cn : [
36279                                 {
36280                                     tag : 'i',
36281                                     cls : 'fa fa-chevron-left'
36282                                 }
36283                             ]
36284                         },
36285                         {
36286                             tag : 'div',
36287                             cls : 'roo-document-slider-thumb',
36288                             cn : [
36289                                 {
36290                                     tag : 'img',
36291                                     cls : 'roo-document-slider-image'
36292                                 }
36293                             ]
36294                         },
36295                         {
36296                             tag : 'div',
36297                             cls : 'roo-document-slider-next',
36298                             cn : [
36299                                 {
36300                                     tag : 'i',
36301                                     cls : 'fa fa-chevron-right'
36302                                 }
36303                             ]
36304                         }
36305                     ]
36306                 }
36307             ]
36308         };
36309         
36310         return cfg;
36311     },
36312     
36313     initEvents : function()
36314     {
36315         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36316         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36317         
36318         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36319         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36320         
36321         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36322         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36323         
36324         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36325         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36326         
36327         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36328         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36329         
36330         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36331         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36332         
36333         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36334         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36335         
36336         this.thumbEl.on('click', this.onClick, this);
36337         
36338         this.prevIndicator.on('click', this.prev, this);
36339         
36340         this.nextIndicator.on('click', this.next, this);
36341         
36342     },
36343     
36344     initial : function()
36345     {
36346         if(this.files.length){
36347             this.indicator = 1;
36348             this.update()
36349         }
36350         
36351         this.fireEvent('initial', this);
36352     },
36353     
36354     update : function()
36355     {
36356         this.imageEl.attr('src', this.files[this.indicator - 1]);
36357         
36358         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36359         
36360         this.prevIndicator.show();
36361         
36362         if(this.indicator == 1){
36363             this.prevIndicator.hide();
36364         }
36365         
36366         this.nextIndicator.show();
36367         
36368         if(this.indicator == this.files.length){
36369             this.nextIndicator.hide();
36370         }
36371         
36372         this.thumbEl.scrollTo('top');
36373         
36374         this.fireEvent('update', this);
36375     },
36376     
36377     onClick : function(e)
36378     {
36379         e.preventDefault();
36380         
36381         this.fireEvent('click', this);
36382     },
36383     
36384     prev : function(e)
36385     {
36386         e.preventDefault();
36387         
36388         this.indicator = Math.max(1, this.indicator - 1);
36389         
36390         this.update();
36391     },
36392     
36393     next : function(e)
36394     {
36395         e.preventDefault();
36396         
36397         this.indicator = Math.min(this.files.length, this.indicator + 1);
36398         
36399         this.update();
36400     }
36401 });
36402 /*
36403  * - LGPL
36404  *
36405  * RadioSet
36406  *
36407  *
36408  */
36409
36410 /**
36411  * @class Roo.bootstrap.RadioSet
36412  * @extends Roo.bootstrap.Input
36413  * Bootstrap RadioSet class
36414  * @cfg {String} indicatorpos (left|right) default left
36415  * @cfg {Boolean} inline (true|false) inline the element (default true)
36416  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36417  * @constructor
36418  * Create a new RadioSet
36419  * @param {Object} config The config object
36420  */
36421
36422 Roo.bootstrap.RadioSet = function(config){
36423     
36424     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36425     
36426     this.radioes = [];
36427     
36428     Roo.bootstrap.RadioSet.register(this);
36429     
36430     this.addEvents({
36431         /**
36432         * @event check
36433         * Fires when the element is checked or unchecked.
36434         * @param {Roo.bootstrap.RadioSet} this This radio
36435         * @param {Roo.bootstrap.Radio} item The checked item
36436         */
36437        check : true,
36438        /**
36439         * @event click
36440         * Fires when the element is click.
36441         * @param {Roo.bootstrap.RadioSet} this This radio set
36442         * @param {Roo.bootstrap.Radio} item The checked item
36443         * @param {Roo.EventObject} e The event object
36444         */
36445        click : true
36446     });
36447     
36448 };
36449
36450 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36451
36452     radioes : false,
36453     
36454     inline : true,
36455     
36456     weight : '',
36457     
36458     indicatorpos : 'left',
36459     
36460     getAutoCreate : function()
36461     {
36462         var label = {
36463             tag : 'label',
36464             cls : 'roo-radio-set-label',
36465             cn : [
36466                 {
36467                     tag : 'span',
36468                     html : this.fieldLabel
36469                 }
36470             ]
36471         };
36472         if (Roo.bootstrap.version == 3) {
36473             
36474             
36475             if(this.indicatorpos == 'left'){
36476                 label.cn.unshift({
36477                     tag : 'i',
36478                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36479                     tooltip : 'This field is required'
36480                 });
36481             } else {
36482                 label.cn.push({
36483                     tag : 'i',
36484                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36485                     tooltip : 'This field is required'
36486                 });
36487             }
36488         }
36489         var items = {
36490             tag : 'div',
36491             cls : 'roo-radio-set-items'
36492         };
36493         
36494         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36495         
36496         if (align === 'left' && this.fieldLabel.length) {
36497             
36498             items = {
36499                 cls : "roo-radio-set-right", 
36500                 cn: [
36501                     items
36502                 ]
36503             };
36504             
36505             if(this.labelWidth > 12){
36506                 label.style = "width: " + this.labelWidth + 'px';
36507             }
36508             
36509             if(this.labelWidth < 13 && this.labelmd == 0){
36510                 this.labelmd = this.labelWidth;
36511             }
36512             
36513             if(this.labellg > 0){
36514                 label.cls += ' col-lg-' + this.labellg;
36515                 items.cls += ' col-lg-' + (12 - this.labellg);
36516             }
36517             
36518             if(this.labelmd > 0){
36519                 label.cls += ' col-md-' + this.labelmd;
36520                 items.cls += ' col-md-' + (12 - this.labelmd);
36521             }
36522             
36523             if(this.labelsm > 0){
36524                 label.cls += ' col-sm-' + this.labelsm;
36525                 items.cls += ' col-sm-' + (12 - this.labelsm);
36526             }
36527             
36528             if(this.labelxs > 0){
36529                 label.cls += ' col-xs-' + this.labelxs;
36530                 items.cls += ' col-xs-' + (12 - this.labelxs);
36531             }
36532         }
36533         
36534         var cfg = {
36535             tag : 'div',
36536             cls : 'roo-radio-set',
36537             cn : [
36538                 {
36539                     tag : 'input',
36540                     cls : 'roo-radio-set-input',
36541                     type : 'hidden',
36542                     name : this.name,
36543                     value : this.value ? this.value :  ''
36544                 },
36545                 label,
36546                 items
36547             ]
36548         };
36549         
36550         if(this.weight.length){
36551             cfg.cls += ' roo-radio-' + this.weight;
36552         }
36553         
36554         if(this.inline) {
36555             cfg.cls += ' roo-radio-set-inline';
36556         }
36557         
36558         var settings=this;
36559         ['xs','sm','md','lg'].map(function(size){
36560             if (settings[size]) {
36561                 cfg.cls += ' col-' + size + '-' + settings[size];
36562             }
36563         });
36564         
36565         return cfg;
36566         
36567     },
36568
36569     initEvents : function()
36570     {
36571         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36572         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36573         
36574         if(!this.fieldLabel.length){
36575             this.labelEl.hide();
36576         }
36577         
36578         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36579         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36580         
36581         this.indicator = this.indicatorEl();
36582         
36583         if(this.indicator){
36584             this.indicator.addClass('invisible');
36585         }
36586         
36587         this.originalValue = this.getValue();
36588         
36589     },
36590     
36591     inputEl: function ()
36592     {
36593         return this.el.select('.roo-radio-set-input', true).first();
36594     },
36595     
36596     getChildContainer : function()
36597     {
36598         return this.itemsEl;
36599     },
36600     
36601     register : function(item)
36602     {
36603         this.radioes.push(item);
36604         
36605     },
36606     
36607     validate : function()
36608     {   
36609         if(this.getVisibilityEl().hasClass('hidden')){
36610             return true;
36611         }
36612         
36613         var valid = false;
36614         
36615         Roo.each(this.radioes, function(i){
36616             if(!i.checked){
36617                 return;
36618             }
36619             
36620             valid = true;
36621             return false;
36622         });
36623         
36624         if(this.allowBlank) {
36625             return true;
36626         }
36627         
36628         if(this.disabled || valid){
36629             this.markValid();
36630             return true;
36631         }
36632         
36633         this.markInvalid();
36634         return false;
36635         
36636     },
36637     
36638     markValid : function()
36639     {
36640         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36641             this.indicatorEl().removeClass('visible');
36642             this.indicatorEl().addClass('invisible');
36643         }
36644         
36645         
36646         if (Roo.bootstrap.version == 3) {
36647             this.el.removeClass([this.invalidClass, this.validClass]);
36648             this.el.addClass(this.validClass);
36649         } else {
36650             this.el.removeClass(['is-invalid','is-valid']);
36651             this.el.addClass(['is-valid']);
36652         }
36653         this.fireEvent('valid', this);
36654     },
36655     
36656     markInvalid : function(msg)
36657     {
36658         if(this.allowBlank || this.disabled){
36659             return;
36660         }
36661         
36662         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36663             this.indicatorEl().removeClass('invisible');
36664             this.indicatorEl().addClass('visible');
36665         }
36666         if (Roo.bootstrap.version == 3) {
36667             this.el.removeClass([this.invalidClass, this.validClass]);
36668             this.el.addClass(this.invalidClass);
36669         } else {
36670             this.el.removeClass(['is-invalid','is-valid']);
36671             this.el.addClass(['is-invalid']);
36672         }
36673         
36674         this.fireEvent('invalid', this, msg);
36675         
36676     },
36677     
36678     setValue : function(v, suppressEvent)
36679     {   
36680         if(this.value === v){
36681             return;
36682         }
36683         
36684         this.value = v;
36685         
36686         if(this.rendered){
36687             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36688         }
36689         
36690         Roo.each(this.radioes, function(i){
36691             i.checked = false;
36692             i.el.removeClass('checked');
36693         });
36694         
36695         Roo.each(this.radioes, function(i){
36696             
36697             if(i.value === v || i.value.toString() === v.toString()){
36698                 i.checked = true;
36699                 i.el.addClass('checked');
36700                 
36701                 if(suppressEvent !== true){
36702                     this.fireEvent('check', this, i);
36703                 }
36704                 
36705                 return false;
36706             }
36707             
36708         }, this);
36709         
36710         this.validate();
36711     },
36712     
36713     clearInvalid : function(){
36714         
36715         if(!this.el || this.preventMark){
36716             return;
36717         }
36718         
36719         this.el.removeClass([this.invalidClass]);
36720         
36721         this.fireEvent('valid', this);
36722     }
36723     
36724 });
36725
36726 Roo.apply(Roo.bootstrap.RadioSet, {
36727     
36728     groups: {},
36729     
36730     register : function(set)
36731     {
36732         this.groups[set.name] = set;
36733     },
36734     
36735     get: function(name) 
36736     {
36737         if (typeof(this.groups[name]) == 'undefined') {
36738             return false;
36739         }
36740         
36741         return this.groups[name] ;
36742     }
36743     
36744 });
36745 /*
36746  * Based on:
36747  * Ext JS Library 1.1.1
36748  * Copyright(c) 2006-2007, Ext JS, LLC.
36749  *
36750  * Originally Released Under LGPL - original licence link has changed is not relivant.
36751  *
36752  * Fork - LGPL
36753  * <script type="text/javascript">
36754  */
36755
36756
36757 /**
36758  * @class Roo.bootstrap.SplitBar
36759  * @extends Roo.util.Observable
36760  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36761  * <br><br>
36762  * Usage:
36763  * <pre><code>
36764 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36765                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36766 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36767 split.minSize = 100;
36768 split.maxSize = 600;
36769 split.animate = true;
36770 split.on('moved', splitterMoved);
36771 </code></pre>
36772  * @constructor
36773  * Create a new SplitBar
36774  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36775  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36776  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36777  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36778                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36779                         position of the SplitBar).
36780  */
36781 Roo.bootstrap.SplitBar = function(cfg){
36782     
36783     /** @private */
36784     
36785     //{
36786     //  dragElement : elm
36787     //  resizingElement: el,
36788         // optional..
36789     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36790     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36791         // existingProxy ???
36792     //}
36793     
36794     this.el = Roo.get(cfg.dragElement, true);
36795     this.el.dom.unselectable = "on";
36796     /** @private */
36797     this.resizingEl = Roo.get(cfg.resizingElement, true);
36798
36799     /**
36800      * @private
36801      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36802      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36803      * @type Number
36804      */
36805     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36806     
36807     /**
36808      * The minimum size of the resizing element. (Defaults to 0)
36809      * @type Number
36810      */
36811     this.minSize = 0;
36812     
36813     /**
36814      * The maximum size of the resizing element. (Defaults to 2000)
36815      * @type Number
36816      */
36817     this.maxSize = 2000;
36818     
36819     /**
36820      * Whether to animate the transition to the new size
36821      * @type Boolean
36822      */
36823     this.animate = false;
36824     
36825     /**
36826      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36827      * @type Boolean
36828      */
36829     this.useShim = false;
36830     
36831     /** @private */
36832     this.shim = null;
36833     
36834     if(!cfg.existingProxy){
36835         /** @private */
36836         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36837     }else{
36838         this.proxy = Roo.get(cfg.existingProxy).dom;
36839     }
36840     /** @private */
36841     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36842     
36843     /** @private */
36844     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36845     
36846     /** @private */
36847     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36848     
36849     /** @private */
36850     this.dragSpecs = {};
36851     
36852     /**
36853      * @private The adapter to use to positon and resize elements
36854      */
36855     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36856     this.adapter.init(this);
36857     
36858     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36859         /** @private */
36860         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36861         this.el.addClass("roo-splitbar-h");
36862     }else{
36863         /** @private */
36864         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36865         this.el.addClass("roo-splitbar-v");
36866     }
36867     
36868     this.addEvents({
36869         /**
36870          * @event resize
36871          * Fires when the splitter is moved (alias for {@link #event-moved})
36872          * @param {Roo.bootstrap.SplitBar} this
36873          * @param {Number} newSize the new width or height
36874          */
36875         "resize" : true,
36876         /**
36877          * @event moved
36878          * Fires when the splitter is moved
36879          * @param {Roo.bootstrap.SplitBar} this
36880          * @param {Number} newSize the new width or height
36881          */
36882         "moved" : true,
36883         /**
36884          * @event beforeresize
36885          * Fires before the splitter is dragged
36886          * @param {Roo.bootstrap.SplitBar} this
36887          */
36888         "beforeresize" : true,
36889
36890         "beforeapply" : true
36891     });
36892
36893     Roo.util.Observable.call(this);
36894 };
36895
36896 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36897     onStartProxyDrag : function(x, y){
36898         this.fireEvent("beforeresize", this);
36899         if(!this.overlay){
36900             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36901             o.unselectable();
36902             o.enableDisplayMode("block");
36903             // all splitbars share the same overlay
36904             Roo.bootstrap.SplitBar.prototype.overlay = o;
36905         }
36906         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36907         this.overlay.show();
36908         Roo.get(this.proxy).setDisplayed("block");
36909         var size = this.adapter.getElementSize(this);
36910         this.activeMinSize = this.getMinimumSize();;
36911         this.activeMaxSize = this.getMaximumSize();;
36912         var c1 = size - this.activeMinSize;
36913         var c2 = Math.max(this.activeMaxSize - size, 0);
36914         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36915             this.dd.resetConstraints();
36916             this.dd.setXConstraint(
36917                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36918                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36919             );
36920             this.dd.setYConstraint(0, 0);
36921         }else{
36922             this.dd.resetConstraints();
36923             this.dd.setXConstraint(0, 0);
36924             this.dd.setYConstraint(
36925                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
36926                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36927             );
36928          }
36929         this.dragSpecs.startSize = size;
36930         this.dragSpecs.startPoint = [x, y];
36931         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36932     },
36933     
36934     /** 
36935      * @private Called after the drag operation by the DDProxy
36936      */
36937     onEndProxyDrag : function(e){
36938         Roo.get(this.proxy).setDisplayed(false);
36939         var endPoint = Roo.lib.Event.getXY(e);
36940         if(this.overlay){
36941             this.overlay.hide();
36942         }
36943         var newSize;
36944         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36945             newSize = this.dragSpecs.startSize + 
36946                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36947                     endPoint[0] - this.dragSpecs.startPoint[0] :
36948                     this.dragSpecs.startPoint[0] - endPoint[0]
36949                 );
36950         }else{
36951             newSize = this.dragSpecs.startSize + 
36952                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36953                     endPoint[1] - this.dragSpecs.startPoint[1] :
36954                     this.dragSpecs.startPoint[1] - endPoint[1]
36955                 );
36956         }
36957         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36958         if(newSize != this.dragSpecs.startSize){
36959             if(this.fireEvent('beforeapply', this, newSize) !== false){
36960                 this.adapter.setElementSize(this, newSize);
36961                 this.fireEvent("moved", this, newSize);
36962                 this.fireEvent("resize", this, newSize);
36963             }
36964         }
36965     },
36966     
36967     /**
36968      * Get the adapter this SplitBar uses
36969      * @return The adapter object
36970      */
36971     getAdapter : function(){
36972         return this.adapter;
36973     },
36974     
36975     /**
36976      * Set the adapter this SplitBar uses
36977      * @param {Object} adapter A SplitBar adapter object
36978      */
36979     setAdapter : function(adapter){
36980         this.adapter = adapter;
36981         this.adapter.init(this);
36982     },
36983     
36984     /**
36985      * Gets the minimum size for the resizing element
36986      * @return {Number} The minimum size
36987      */
36988     getMinimumSize : function(){
36989         return this.minSize;
36990     },
36991     
36992     /**
36993      * Sets the minimum size for the resizing element
36994      * @param {Number} minSize The minimum size
36995      */
36996     setMinimumSize : function(minSize){
36997         this.minSize = minSize;
36998     },
36999     
37000     /**
37001      * Gets the maximum size for the resizing element
37002      * @return {Number} The maximum size
37003      */
37004     getMaximumSize : function(){
37005         return this.maxSize;
37006     },
37007     
37008     /**
37009      * Sets the maximum size for the resizing element
37010      * @param {Number} maxSize The maximum size
37011      */
37012     setMaximumSize : function(maxSize){
37013         this.maxSize = maxSize;
37014     },
37015     
37016     /**
37017      * Sets the initialize size for the resizing element
37018      * @param {Number} size The initial size
37019      */
37020     setCurrentSize : function(size){
37021         var oldAnimate = this.animate;
37022         this.animate = false;
37023         this.adapter.setElementSize(this, size);
37024         this.animate = oldAnimate;
37025     },
37026     
37027     /**
37028      * Destroy this splitbar. 
37029      * @param {Boolean} removeEl True to remove the element
37030      */
37031     destroy : function(removeEl){
37032         if(this.shim){
37033             this.shim.remove();
37034         }
37035         this.dd.unreg();
37036         this.proxy.parentNode.removeChild(this.proxy);
37037         if(removeEl){
37038             this.el.remove();
37039         }
37040     }
37041 });
37042
37043 /**
37044  * @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.
37045  */
37046 Roo.bootstrap.SplitBar.createProxy = function(dir){
37047     var proxy = new Roo.Element(document.createElement("div"));
37048     proxy.unselectable();
37049     var cls = 'roo-splitbar-proxy';
37050     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37051     document.body.appendChild(proxy.dom);
37052     return proxy.dom;
37053 };
37054
37055 /** 
37056  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37057  * Default Adapter. It assumes the splitter and resizing element are not positioned
37058  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37059  */
37060 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37061 };
37062
37063 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37064     // do nothing for now
37065     init : function(s){
37066     
37067     },
37068     /**
37069      * Called before drag operations to get the current size of the resizing element. 
37070      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37071      */
37072      getElementSize : function(s){
37073         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37074             return s.resizingEl.getWidth();
37075         }else{
37076             return s.resizingEl.getHeight();
37077         }
37078     },
37079     
37080     /**
37081      * Called after drag operations to set the size of the resizing element.
37082      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37083      * @param {Number} newSize The new size to set
37084      * @param {Function} onComplete A function to be invoked when resizing is complete
37085      */
37086     setElementSize : function(s, newSize, onComplete){
37087         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37088             if(!s.animate){
37089                 s.resizingEl.setWidth(newSize);
37090                 if(onComplete){
37091                     onComplete(s, newSize);
37092                 }
37093             }else{
37094                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37095             }
37096         }else{
37097             
37098             if(!s.animate){
37099                 s.resizingEl.setHeight(newSize);
37100                 if(onComplete){
37101                     onComplete(s, newSize);
37102                 }
37103             }else{
37104                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37105             }
37106         }
37107     }
37108 };
37109
37110 /** 
37111  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37112  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37113  * Adapter that  moves the splitter element to align with the resized sizing element. 
37114  * Used with an absolute positioned SplitBar.
37115  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37116  * document.body, make sure you assign an id to the body element.
37117  */
37118 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37119     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37120     this.container = Roo.get(container);
37121 };
37122
37123 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37124     init : function(s){
37125         this.basic.init(s);
37126     },
37127     
37128     getElementSize : function(s){
37129         return this.basic.getElementSize(s);
37130     },
37131     
37132     setElementSize : function(s, newSize, onComplete){
37133         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37134     },
37135     
37136     moveSplitter : function(s){
37137         var yes = Roo.bootstrap.SplitBar;
37138         switch(s.placement){
37139             case yes.LEFT:
37140                 s.el.setX(s.resizingEl.getRight());
37141                 break;
37142             case yes.RIGHT:
37143                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37144                 break;
37145             case yes.TOP:
37146                 s.el.setY(s.resizingEl.getBottom());
37147                 break;
37148             case yes.BOTTOM:
37149                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37150                 break;
37151         }
37152     }
37153 };
37154
37155 /**
37156  * Orientation constant - Create a vertical SplitBar
37157  * @static
37158  * @type Number
37159  */
37160 Roo.bootstrap.SplitBar.VERTICAL = 1;
37161
37162 /**
37163  * Orientation constant - Create a horizontal SplitBar
37164  * @static
37165  * @type Number
37166  */
37167 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37168
37169 /**
37170  * Placement constant - The resizing element is to the left of the splitter element
37171  * @static
37172  * @type Number
37173  */
37174 Roo.bootstrap.SplitBar.LEFT = 1;
37175
37176 /**
37177  * Placement constant - The resizing element is to the right of the splitter element
37178  * @static
37179  * @type Number
37180  */
37181 Roo.bootstrap.SplitBar.RIGHT = 2;
37182
37183 /**
37184  * Placement constant - The resizing element is positioned above the splitter element
37185  * @static
37186  * @type Number
37187  */
37188 Roo.bootstrap.SplitBar.TOP = 3;
37189
37190 /**
37191  * Placement constant - The resizing element is positioned under splitter element
37192  * @static
37193  * @type Number
37194  */
37195 Roo.bootstrap.SplitBar.BOTTOM = 4;
37196 Roo.namespace("Roo.bootstrap.layout");/*
37197  * Based on:
37198  * Ext JS Library 1.1.1
37199  * Copyright(c) 2006-2007, Ext JS, LLC.
37200  *
37201  * Originally Released Under LGPL - original licence link has changed is not relivant.
37202  *
37203  * Fork - LGPL
37204  * <script type="text/javascript">
37205  */
37206
37207 /**
37208  * @class Roo.bootstrap.layout.Manager
37209  * @extends Roo.bootstrap.Component
37210  * Base class for layout managers.
37211  */
37212 Roo.bootstrap.layout.Manager = function(config)
37213 {
37214     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37215
37216
37217
37218
37219
37220     /** false to disable window resize monitoring @type Boolean */
37221     this.monitorWindowResize = true;
37222     this.regions = {};
37223     this.addEvents({
37224         /**
37225          * @event layout
37226          * Fires when a layout is performed.
37227          * @param {Roo.LayoutManager} this
37228          */
37229         "layout" : true,
37230         /**
37231          * @event regionresized
37232          * Fires when the user resizes a region.
37233          * @param {Roo.LayoutRegion} region The resized region
37234          * @param {Number} newSize The new size (width for east/west, height for north/south)
37235          */
37236         "regionresized" : true,
37237         /**
37238          * @event regioncollapsed
37239          * Fires when a region is collapsed.
37240          * @param {Roo.LayoutRegion} region The collapsed region
37241          */
37242         "regioncollapsed" : true,
37243         /**
37244          * @event regionexpanded
37245          * Fires when a region is expanded.
37246          * @param {Roo.LayoutRegion} region The expanded region
37247          */
37248         "regionexpanded" : true
37249     });
37250     this.updating = false;
37251
37252     if (config.el) {
37253         this.el = Roo.get(config.el);
37254         this.initEvents();
37255     }
37256
37257 };
37258
37259 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37260
37261
37262     regions : null,
37263
37264     monitorWindowResize : true,
37265
37266
37267     updating : false,
37268
37269
37270     onRender : function(ct, position)
37271     {
37272         if(!this.el){
37273             this.el = Roo.get(ct);
37274             this.initEvents();
37275         }
37276         //this.fireEvent('render',this);
37277     },
37278
37279
37280     initEvents: function()
37281     {
37282
37283
37284         // ie scrollbar fix
37285         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37286             document.body.scroll = "no";
37287         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37288             this.el.position('relative');
37289         }
37290         this.id = this.el.id;
37291         this.el.addClass("roo-layout-container");
37292         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37293         if(this.el.dom != document.body ) {
37294             this.el.on('resize', this.layout,this);
37295             this.el.on('show', this.layout,this);
37296         }
37297
37298     },
37299
37300     /**
37301      * Returns true if this layout is currently being updated
37302      * @return {Boolean}
37303      */
37304     isUpdating : function(){
37305         return this.updating;
37306     },
37307
37308     /**
37309      * Suspend the LayoutManager from doing auto-layouts while
37310      * making multiple add or remove calls
37311      */
37312     beginUpdate : function(){
37313         this.updating = true;
37314     },
37315
37316     /**
37317      * Restore auto-layouts and optionally disable the manager from performing a layout
37318      * @param {Boolean} noLayout true to disable a layout update
37319      */
37320     endUpdate : function(noLayout){
37321         this.updating = false;
37322         if(!noLayout){
37323             this.layout();
37324         }
37325     },
37326
37327     layout: function(){
37328         // abstract...
37329     },
37330
37331     onRegionResized : function(region, newSize){
37332         this.fireEvent("regionresized", region, newSize);
37333         this.layout();
37334     },
37335
37336     onRegionCollapsed : function(region){
37337         this.fireEvent("regioncollapsed", region);
37338     },
37339
37340     onRegionExpanded : function(region){
37341         this.fireEvent("regionexpanded", region);
37342     },
37343
37344     /**
37345      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37346      * performs box-model adjustments.
37347      * @return {Object} The size as an object {width: (the width), height: (the height)}
37348      */
37349     getViewSize : function()
37350     {
37351         var size;
37352         if(this.el.dom != document.body){
37353             size = this.el.getSize();
37354         }else{
37355             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37356         }
37357         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37358         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37359         return size;
37360     },
37361
37362     /**
37363      * Returns the Element this layout is bound to.
37364      * @return {Roo.Element}
37365      */
37366     getEl : function(){
37367         return this.el;
37368     },
37369
37370     /**
37371      * Returns the specified region.
37372      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37373      * @return {Roo.LayoutRegion}
37374      */
37375     getRegion : function(target){
37376         return this.regions[target.toLowerCase()];
37377     },
37378
37379     onWindowResize : function(){
37380         if(this.monitorWindowResize){
37381             this.layout();
37382         }
37383     }
37384 });
37385 /*
37386  * Based on:
37387  * Ext JS Library 1.1.1
37388  * Copyright(c) 2006-2007, Ext JS, LLC.
37389  *
37390  * Originally Released Under LGPL - original licence link has changed is not relivant.
37391  *
37392  * Fork - LGPL
37393  * <script type="text/javascript">
37394  */
37395 /**
37396  * @class Roo.bootstrap.layout.Border
37397  * @extends Roo.bootstrap.layout.Manager
37398  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37399  * please see: examples/bootstrap/nested.html<br><br>
37400  
37401 <b>The container the layout is rendered into can be either the body element or any other element.
37402 If it is not the body element, the container needs to either be an absolute positioned element,
37403 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37404 the container size if it is not the body element.</b>
37405
37406 * @constructor
37407 * Create a new Border
37408 * @param {Object} config Configuration options
37409  */
37410 Roo.bootstrap.layout.Border = function(config){
37411     config = config || {};
37412     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37413     
37414     
37415     
37416     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37417         if(config[region]){
37418             config[region].region = region;
37419             this.addRegion(config[region]);
37420         }
37421     },this);
37422     
37423 };
37424
37425 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
37426
37427 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37428     
37429     parent : false, // this might point to a 'nest' or a ???
37430     
37431     /**
37432      * Creates and adds a new region if it doesn't already exist.
37433      * @param {String} target The target region key (north, south, east, west or center).
37434      * @param {Object} config The regions config object
37435      * @return {BorderLayoutRegion} The new region
37436      */
37437     addRegion : function(config)
37438     {
37439         if(!this.regions[config.region]){
37440             var r = this.factory(config);
37441             this.bindRegion(r);
37442         }
37443         return this.regions[config.region];
37444     },
37445
37446     // private (kinda)
37447     bindRegion : function(r){
37448         this.regions[r.config.region] = r;
37449         
37450         r.on("visibilitychange",    this.layout, this);
37451         r.on("paneladded",          this.layout, this);
37452         r.on("panelremoved",        this.layout, this);
37453         r.on("invalidated",         this.layout, this);
37454         r.on("resized",             this.onRegionResized, this);
37455         r.on("collapsed",           this.onRegionCollapsed, this);
37456         r.on("expanded",            this.onRegionExpanded, this);
37457     },
37458
37459     /**
37460      * Performs a layout update.
37461      */
37462     layout : function()
37463     {
37464         if(this.updating) {
37465             return;
37466         }
37467         
37468         // render all the rebions if they have not been done alreayd?
37469         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37470             if(this.regions[region] && !this.regions[region].bodyEl){
37471                 this.regions[region].onRender(this.el)
37472             }
37473         },this);
37474         
37475         var size = this.getViewSize();
37476         var w = size.width;
37477         var h = size.height;
37478         var centerW = w;
37479         var centerH = h;
37480         var centerY = 0;
37481         var centerX = 0;
37482         //var x = 0, y = 0;
37483
37484         var rs = this.regions;
37485         var north = rs["north"];
37486         var south = rs["south"]; 
37487         var west = rs["west"];
37488         var east = rs["east"];
37489         var center = rs["center"];
37490         //if(this.hideOnLayout){ // not supported anymore
37491             //c.el.setStyle("display", "none");
37492         //}
37493         if(north && north.isVisible()){
37494             var b = north.getBox();
37495             var m = north.getMargins();
37496             b.width = w - (m.left+m.right);
37497             b.x = m.left;
37498             b.y = m.top;
37499             centerY = b.height + b.y + m.bottom;
37500             centerH -= centerY;
37501             north.updateBox(this.safeBox(b));
37502         }
37503         if(south && south.isVisible()){
37504             var b = south.getBox();
37505             var m = south.getMargins();
37506             b.width = w - (m.left+m.right);
37507             b.x = m.left;
37508             var totalHeight = (b.height + m.top + m.bottom);
37509             b.y = h - totalHeight + m.top;
37510             centerH -= totalHeight;
37511             south.updateBox(this.safeBox(b));
37512         }
37513         if(west && west.isVisible()){
37514             var b = west.getBox();
37515             var m = west.getMargins();
37516             b.height = centerH - (m.top+m.bottom);
37517             b.x = m.left;
37518             b.y = centerY + m.top;
37519             var totalWidth = (b.width + m.left + m.right);
37520             centerX += totalWidth;
37521             centerW -= totalWidth;
37522             west.updateBox(this.safeBox(b));
37523         }
37524         if(east && east.isVisible()){
37525             var b = east.getBox();
37526             var m = east.getMargins();
37527             b.height = centerH - (m.top+m.bottom);
37528             var totalWidth = (b.width + m.left + m.right);
37529             b.x = w - totalWidth + m.left;
37530             b.y = centerY + m.top;
37531             centerW -= totalWidth;
37532             east.updateBox(this.safeBox(b));
37533         }
37534         if(center){
37535             var m = center.getMargins();
37536             var centerBox = {
37537                 x: centerX + m.left,
37538                 y: centerY + m.top,
37539                 width: centerW - (m.left+m.right),
37540                 height: centerH - (m.top+m.bottom)
37541             };
37542             //if(this.hideOnLayout){
37543                 //center.el.setStyle("display", "block");
37544             //}
37545             center.updateBox(this.safeBox(centerBox));
37546         }
37547         this.el.repaint();
37548         this.fireEvent("layout", this);
37549     },
37550
37551     // private
37552     safeBox : function(box){
37553         box.width = Math.max(0, box.width);
37554         box.height = Math.max(0, box.height);
37555         return box;
37556     },
37557
37558     /**
37559      * Adds a ContentPanel (or subclass) to this layout.
37560      * @param {String} target The target region key (north, south, east, west or center).
37561      * @param {Roo.ContentPanel} panel The panel to add
37562      * @return {Roo.ContentPanel} The added panel
37563      */
37564     add : function(target, panel){
37565          
37566         target = target.toLowerCase();
37567         return this.regions[target].add(panel);
37568     },
37569
37570     /**
37571      * Remove a ContentPanel (or subclass) to this layout.
37572      * @param {String} target The target region key (north, south, east, west or center).
37573      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37574      * @return {Roo.ContentPanel} The removed panel
37575      */
37576     remove : function(target, panel){
37577         target = target.toLowerCase();
37578         return this.regions[target].remove(panel);
37579     },
37580
37581     /**
37582      * Searches all regions for a panel with the specified id
37583      * @param {String} panelId
37584      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37585      */
37586     findPanel : function(panelId){
37587         var rs = this.regions;
37588         for(var target in rs){
37589             if(typeof rs[target] != "function"){
37590                 var p = rs[target].getPanel(panelId);
37591                 if(p){
37592                     return p;
37593                 }
37594             }
37595         }
37596         return null;
37597     },
37598
37599     /**
37600      * Searches all regions for a panel with the specified id and activates (shows) it.
37601      * @param {String/ContentPanel} panelId The panels id or the panel itself
37602      * @return {Roo.ContentPanel} The shown panel or null
37603      */
37604     showPanel : function(panelId) {
37605       var rs = this.regions;
37606       for(var target in rs){
37607          var r = rs[target];
37608          if(typeof r != "function"){
37609             if(r.hasPanel(panelId)){
37610                return r.showPanel(panelId);
37611             }
37612          }
37613       }
37614       return null;
37615    },
37616
37617    /**
37618      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37619      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37620      */
37621    /*
37622     restoreState : function(provider){
37623         if(!provider){
37624             provider = Roo.state.Manager;
37625         }
37626         var sm = new Roo.LayoutStateManager();
37627         sm.init(this, provider);
37628     },
37629 */
37630  
37631  
37632     /**
37633      * Adds a xtype elements to the layout.
37634      * <pre><code>
37635
37636 layout.addxtype({
37637        xtype : 'ContentPanel',
37638        region: 'west',
37639        items: [ .... ]
37640    }
37641 );
37642
37643 layout.addxtype({
37644         xtype : 'NestedLayoutPanel',
37645         region: 'west',
37646         layout: {
37647            center: { },
37648            west: { }   
37649         },
37650         items : [ ... list of content panels or nested layout panels.. ]
37651    }
37652 );
37653 </code></pre>
37654      * @param {Object} cfg Xtype definition of item to add.
37655      */
37656     addxtype : function(cfg)
37657     {
37658         // basically accepts a pannel...
37659         // can accept a layout region..!?!?
37660         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37661         
37662         
37663         // theory?  children can only be panels??
37664         
37665         //if (!cfg.xtype.match(/Panel$/)) {
37666         //    return false;
37667         //}
37668         var ret = false;
37669         
37670         if (typeof(cfg.region) == 'undefined') {
37671             Roo.log("Failed to add Panel, region was not set");
37672             Roo.log(cfg);
37673             return false;
37674         }
37675         var region = cfg.region;
37676         delete cfg.region;
37677         
37678           
37679         var xitems = [];
37680         if (cfg.items) {
37681             xitems = cfg.items;
37682             delete cfg.items;
37683         }
37684         var nb = false;
37685         
37686         if ( region == 'center') {
37687             Roo.log("Center: " + cfg.title);
37688         }
37689         
37690         
37691         switch(cfg.xtype) 
37692         {
37693             case 'Content':  // ContentPanel (el, cfg)
37694             case 'Scroll':  // ContentPanel (el, cfg)
37695             case 'View': 
37696                 cfg.autoCreate = cfg.autoCreate || true;
37697                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37698                 //} else {
37699                 //    var el = this.el.createChild();
37700                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37701                 //}
37702                 
37703                 this.add(region, ret);
37704                 break;
37705             
37706             /*
37707             case 'TreePanel': // our new panel!
37708                 cfg.el = this.el.createChild();
37709                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37710                 this.add(region, ret);
37711                 break;
37712             */
37713             
37714             case 'Nest': 
37715                 // create a new Layout (which is  a Border Layout...
37716                 
37717                 var clayout = cfg.layout;
37718                 clayout.el  = this.el.createChild();
37719                 clayout.items   = clayout.items  || [];
37720                 
37721                 delete cfg.layout;
37722                 
37723                 // replace this exitems with the clayout ones..
37724                 xitems = clayout.items;
37725                  
37726                 // force background off if it's in center...
37727                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37728                     cfg.background = false;
37729                 }
37730                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37731                 
37732                 
37733                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37734                 //console.log('adding nested layout panel '  + cfg.toSource());
37735                 this.add(region, ret);
37736                 nb = {}; /// find first...
37737                 break;
37738             
37739             case 'Grid':
37740                 
37741                 // needs grid and region
37742                 
37743                 //var el = this.getRegion(region).el.createChild();
37744                 /*
37745                  *var el = this.el.createChild();
37746                 // create the grid first...
37747                 cfg.grid.container = el;
37748                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37749                 */
37750                 
37751                 if (region == 'center' && this.active ) {
37752                     cfg.background = false;
37753                 }
37754                 
37755                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37756                 
37757                 this.add(region, ret);
37758                 /*
37759                 if (cfg.background) {
37760                     // render grid on panel activation (if panel background)
37761                     ret.on('activate', function(gp) {
37762                         if (!gp.grid.rendered) {
37763                     //        gp.grid.render(el);
37764                         }
37765                     });
37766                 } else {
37767                   //  cfg.grid.render(el);
37768                 }
37769                 */
37770                 break;
37771            
37772            
37773             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37774                 // it was the old xcomponent building that caused this before.
37775                 // espeically if border is the top element in the tree.
37776                 ret = this;
37777                 break; 
37778                 
37779                     
37780                 
37781                 
37782                 
37783             default:
37784                 /*
37785                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37786                     
37787                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37788                     this.add(region, ret);
37789                 } else {
37790                 */
37791                     Roo.log(cfg);
37792                     throw "Can not add '" + cfg.xtype + "' to Border";
37793                     return null;
37794              
37795                                 
37796              
37797         }
37798         this.beginUpdate();
37799         // add children..
37800         var region = '';
37801         var abn = {};
37802         Roo.each(xitems, function(i)  {
37803             region = nb && i.region ? i.region : false;
37804             
37805             var add = ret.addxtype(i);
37806            
37807             if (region) {
37808                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37809                 if (!i.background) {
37810                     abn[region] = nb[region] ;
37811                 }
37812             }
37813             
37814         });
37815         this.endUpdate();
37816
37817         // make the last non-background panel active..
37818         //if (nb) { Roo.log(abn); }
37819         if (nb) {
37820             
37821             for(var r in abn) {
37822                 region = this.getRegion(r);
37823                 if (region) {
37824                     // tried using nb[r], but it does not work..
37825                      
37826                     region.showPanel(abn[r]);
37827                    
37828                 }
37829             }
37830         }
37831         return ret;
37832         
37833     },
37834     
37835     
37836 // private
37837     factory : function(cfg)
37838     {
37839         
37840         var validRegions = Roo.bootstrap.layout.Border.regions;
37841
37842         var target = cfg.region;
37843         cfg.mgr = this;
37844         
37845         var r = Roo.bootstrap.layout;
37846         Roo.log(target);
37847         switch(target){
37848             case "north":
37849                 return new r.North(cfg);
37850             case "south":
37851                 return new r.South(cfg);
37852             case "east":
37853                 return new r.East(cfg);
37854             case "west":
37855                 return new r.West(cfg);
37856             case "center":
37857                 return new r.Center(cfg);
37858         }
37859         throw 'Layout region "'+target+'" not supported.';
37860     }
37861     
37862     
37863 });
37864  /*
37865  * Based on:
37866  * Ext JS Library 1.1.1
37867  * Copyright(c) 2006-2007, Ext JS, LLC.
37868  *
37869  * Originally Released Under LGPL - original licence link has changed is not relivant.
37870  *
37871  * Fork - LGPL
37872  * <script type="text/javascript">
37873  */
37874  
37875 /**
37876  * @class Roo.bootstrap.layout.Basic
37877  * @extends Roo.util.Observable
37878  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37879  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37880  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37881  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37882  * @cfg {string}   region  the region that it inhabits..
37883  * @cfg {bool}   skipConfig skip config?
37884  * 
37885
37886  */
37887 Roo.bootstrap.layout.Basic = function(config){
37888     
37889     this.mgr = config.mgr;
37890     
37891     this.position = config.region;
37892     
37893     var skipConfig = config.skipConfig;
37894     
37895     this.events = {
37896         /**
37897          * @scope Roo.BasicLayoutRegion
37898          */
37899         
37900         /**
37901          * @event beforeremove
37902          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37903          * @param {Roo.LayoutRegion} this
37904          * @param {Roo.ContentPanel} panel The panel
37905          * @param {Object} e The cancel event object
37906          */
37907         "beforeremove" : true,
37908         /**
37909          * @event invalidated
37910          * Fires when the layout for this region is changed.
37911          * @param {Roo.LayoutRegion} this
37912          */
37913         "invalidated" : true,
37914         /**
37915          * @event visibilitychange
37916          * Fires when this region is shown or hidden 
37917          * @param {Roo.LayoutRegion} this
37918          * @param {Boolean} visibility true or false
37919          */
37920         "visibilitychange" : true,
37921         /**
37922          * @event paneladded
37923          * Fires when a panel is added. 
37924          * @param {Roo.LayoutRegion} this
37925          * @param {Roo.ContentPanel} panel The panel
37926          */
37927         "paneladded" : true,
37928         /**
37929          * @event panelremoved
37930          * Fires when a panel is removed. 
37931          * @param {Roo.LayoutRegion} this
37932          * @param {Roo.ContentPanel} panel The panel
37933          */
37934         "panelremoved" : true,
37935         /**
37936          * @event beforecollapse
37937          * Fires when this region before collapse.
37938          * @param {Roo.LayoutRegion} this
37939          */
37940         "beforecollapse" : true,
37941         /**
37942          * @event collapsed
37943          * Fires when this region is collapsed.
37944          * @param {Roo.LayoutRegion} this
37945          */
37946         "collapsed" : true,
37947         /**
37948          * @event expanded
37949          * Fires when this region is expanded.
37950          * @param {Roo.LayoutRegion} this
37951          */
37952         "expanded" : true,
37953         /**
37954          * @event slideshow
37955          * Fires when this region is slid into view.
37956          * @param {Roo.LayoutRegion} this
37957          */
37958         "slideshow" : true,
37959         /**
37960          * @event slidehide
37961          * Fires when this region slides out of view. 
37962          * @param {Roo.LayoutRegion} this
37963          */
37964         "slidehide" : true,
37965         /**
37966          * @event panelactivated
37967          * Fires when a panel is activated. 
37968          * @param {Roo.LayoutRegion} this
37969          * @param {Roo.ContentPanel} panel The activated panel
37970          */
37971         "panelactivated" : true,
37972         /**
37973          * @event resized
37974          * Fires when the user resizes this region. 
37975          * @param {Roo.LayoutRegion} this
37976          * @param {Number} newSize The new size (width for east/west, height for north/south)
37977          */
37978         "resized" : true
37979     };
37980     /** A collection of panels in this region. @type Roo.util.MixedCollection */
37981     this.panels = new Roo.util.MixedCollection();
37982     this.panels.getKey = this.getPanelId.createDelegate(this);
37983     this.box = null;
37984     this.activePanel = null;
37985     // ensure listeners are added...
37986     
37987     if (config.listeners || config.events) {
37988         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37989             listeners : config.listeners || {},
37990             events : config.events || {}
37991         });
37992     }
37993     
37994     if(skipConfig !== true){
37995         this.applyConfig(config);
37996     }
37997 };
37998
37999 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38000 {
38001     getPanelId : function(p){
38002         return p.getId();
38003     },
38004     
38005     applyConfig : function(config){
38006         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38007         this.config = config;
38008         
38009     },
38010     
38011     /**
38012      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38013      * the width, for horizontal (north, south) the height.
38014      * @param {Number} newSize The new width or height
38015      */
38016     resizeTo : function(newSize){
38017         var el = this.el ? this.el :
38018                  (this.activePanel ? this.activePanel.getEl() : null);
38019         if(el){
38020             switch(this.position){
38021                 case "east":
38022                 case "west":
38023                     el.setWidth(newSize);
38024                     this.fireEvent("resized", this, newSize);
38025                 break;
38026                 case "north":
38027                 case "south":
38028                     el.setHeight(newSize);
38029                     this.fireEvent("resized", this, newSize);
38030                 break;                
38031             }
38032         }
38033     },
38034     
38035     getBox : function(){
38036         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38037     },
38038     
38039     getMargins : function(){
38040         return this.margins;
38041     },
38042     
38043     updateBox : function(box){
38044         this.box = box;
38045         var el = this.activePanel.getEl();
38046         el.dom.style.left = box.x + "px";
38047         el.dom.style.top = box.y + "px";
38048         this.activePanel.setSize(box.width, box.height);
38049     },
38050     
38051     /**
38052      * Returns the container element for this region.
38053      * @return {Roo.Element}
38054      */
38055     getEl : function(){
38056         return this.activePanel;
38057     },
38058     
38059     /**
38060      * Returns true if this region is currently visible.
38061      * @return {Boolean}
38062      */
38063     isVisible : function(){
38064         return this.activePanel ? true : false;
38065     },
38066     
38067     setActivePanel : function(panel){
38068         panel = this.getPanel(panel);
38069         if(this.activePanel && this.activePanel != panel){
38070             this.activePanel.setActiveState(false);
38071             this.activePanel.getEl().setLeftTop(-10000,-10000);
38072         }
38073         this.activePanel = panel;
38074         panel.setActiveState(true);
38075         if(this.box){
38076             panel.setSize(this.box.width, this.box.height);
38077         }
38078         this.fireEvent("panelactivated", this, panel);
38079         this.fireEvent("invalidated");
38080     },
38081     
38082     /**
38083      * Show the specified panel.
38084      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38085      * @return {Roo.ContentPanel} The shown panel or null
38086      */
38087     showPanel : function(panel){
38088         panel = this.getPanel(panel);
38089         if(panel){
38090             this.setActivePanel(panel);
38091         }
38092         return panel;
38093     },
38094     
38095     /**
38096      * Get the active panel for this region.
38097      * @return {Roo.ContentPanel} The active panel or null
38098      */
38099     getActivePanel : function(){
38100         return this.activePanel;
38101     },
38102     
38103     /**
38104      * Add the passed ContentPanel(s)
38105      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38106      * @return {Roo.ContentPanel} The panel added (if only one was added)
38107      */
38108     add : function(panel){
38109         if(arguments.length > 1){
38110             for(var i = 0, len = arguments.length; i < len; i++) {
38111                 this.add(arguments[i]);
38112             }
38113             return null;
38114         }
38115         if(this.hasPanel(panel)){
38116             this.showPanel(panel);
38117             return panel;
38118         }
38119         var el = panel.getEl();
38120         if(el.dom.parentNode != this.mgr.el.dom){
38121             this.mgr.el.dom.appendChild(el.dom);
38122         }
38123         if(panel.setRegion){
38124             panel.setRegion(this);
38125         }
38126         this.panels.add(panel);
38127         el.setStyle("position", "absolute");
38128         if(!panel.background){
38129             this.setActivePanel(panel);
38130             if(this.config.initialSize && this.panels.getCount()==1){
38131                 this.resizeTo(this.config.initialSize);
38132             }
38133         }
38134         this.fireEvent("paneladded", this, panel);
38135         return panel;
38136     },
38137     
38138     /**
38139      * Returns true if the panel is in this region.
38140      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38141      * @return {Boolean}
38142      */
38143     hasPanel : function(panel){
38144         if(typeof panel == "object"){ // must be panel obj
38145             panel = panel.getId();
38146         }
38147         return this.getPanel(panel) ? true : false;
38148     },
38149     
38150     /**
38151      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38152      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38153      * @param {Boolean} preservePanel Overrides the config preservePanel option
38154      * @return {Roo.ContentPanel} The panel that was removed
38155      */
38156     remove : function(panel, preservePanel){
38157         panel = this.getPanel(panel);
38158         if(!panel){
38159             return null;
38160         }
38161         var e = {};
38162         this.fireEvent("beforeremove", this, panel, e);
38163         if(e.cancel === true){
38164             return null;
38165         }
38166         var panelId = panel.getId();
38167         this.panels.removeKey(panelId);
38168         return panel;
38169     },
38170     
38171     /**
38172      * Returns the panel specified or null if it's not in this region.
38173      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38174      * @return {Roo.ContentPanel}
38175      */
38176     getPanel : function(id){
38177         if(typeof id == "object"){ // must be panel obj
38178             return id;
38179         }
38180         return this.panels.get(id);
38181     },
38182     
38183     /**
38184      * Returns this regions position (north/south/east/west/center).
38185      * @return {String} 
38186      */
38187     getPosition: function(){
38188         return this.position;    
38189     }
38190 });/*
38191  * Based on:
38192  * Ext JS Library 1.1.1
38193  * Copyright(c) 2006-2007, Ext JS, LLC.
38194  *
38195  * Originally Released Under LGPL - original licence link has changed is not relivant.
38196  *
38197  * Fork - LGPL
38198  * <script type="text/javascript">
38199  */
38200  
38201 /**
38202  * @class Roo.bootstrap.layout.Region
38203  * @extends Roo.bootstrap.layout.Basic
38204  * This class represents a region in a layout manager.
38205  
38206  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38207  * @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})
38208  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38209  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38210  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38211  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38212  * @cfg {String}    title           The title for the region (overrides panel titles)
38213  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38214  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38215  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38216  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38217  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38218  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38219  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38220  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38221  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38222  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38223
38224  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38225  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38226  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38227  * @cfg {Number}    width           For East/West panels
38228  * @cfg {Number}    height          For North/South panels
38229  * @cfg {Boolean}   split           To show the splitter
38230  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38231  * 
38232  * @cfg {string}   cls             Extra CSS classes to add to region
38233  * 
38234  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38235  * @cfg {string}   region  the region that it inhabits..
38236  *
38237
38238  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38239  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38240
38241  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38242  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38243  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38244  */
38245 Roo.bootstrap.layout.Region = function(config)
38246 {
38247     this.applyConfig(config);
38248
38249     var mgr = config.mgr;
38250     var pos = config.region;
38251     config.skipConfig = true;
38252     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38253     
38254     if (mgr.el) {
38255         this.onRender(mgr.el);   
38256     }
38257      
38258     this.visible = true;
38259     this.collapsed = false;
38260     this.unrendered_panels = [];
38261 };
38262
38263 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38264
38265     position: '', // set by wrapper (eg. north/south etc..)
38266     unrendered_panels : null,  // unrendered panels.
38267     
38268     tabPosition : false,
38269     
38270     mgr: false, // points to 'Border'
38271     
38272     
38273     createBody : function(){
38274         /** This region's body element 
38275         * @type Roo.Element */
38276         this.bodyEl = this.el.createChild({
38277                 tag: "div",
38278                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38279         });
38280     },
38281
38282     onRender: function(ctr, pos)
38283     {
38284         var dh = Roo.DomHelper;
38285         /** This region's container element 
38286         * @type Roo.Element */
38287         this.el = dh.append(ctr.dom, {
38288                 tag: "div",
38289                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38290             }, true);
38291         /** This region's title element 
38292         * @type Roo.Element */
38293     
38294         this.titleEl = dh.append(this.el.dom,  {
38295                 tag: "div",
38296                 unselectable: "on",
38297                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38298                 children:[
38299                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38300                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38301                 ]
38302             }, true);
38303         
38304         this.titleEl.enableDisplayMode();
38305         /** This region's title text element 
38306         * @type HTMLElement */
38307         this.titleTextEl = this.titleEl.dom.firstChild;
38308         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38309         /*
38310         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38311         this.closeBtn.enableDisplayMode();
38312         this.closeBtn.on("click", this.closeClicked, this);
38313         this.closeBtn.hide();
38314     */
38315         this.createBody(this.config);
38316         if(this.config.hideWhenEmpty){
38317             this.hide();
38318             this.on("paneladded", this.validateVisibility, this);
38319             this.on("panelremoved", this.validateVisibility, this);
38320         }
38321         if(this.autoScroll){
38322             this.bodyEl.setStyle("overflow", "auto");
38323         }else{
38324             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38325         }
38326         //if(c.titlebar !== false){
38327             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38328                 this.titleEl.hide();
38329             }else{
38330                 this.titleEl.show();
38331                 if(this.config.title){
38332                     this.titleTextEl.innerHTML = this.config.title;
38333                 }
38334             }
38335         //}
38336         if(this.config.collapsed){
38337             this.collapse(true);
38338         }
38339         if(this.config.hidden){
38340             this.hide();
38341         }
38342         
38343         if (this.unrendered_panels && this.unrendered_panels.length) {
38344             for (var i =0;i< this.unrendered_panels.length; i++) {
38345                 this.add(this.unrendered_panels[i]);
38346             }
38347             this.unrendered_panels = null;
38348             
38349         }
38350         
38351     },
38352     
38353     applyConfig : function(c)
38354     {
38355         /*
38356          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38357             var dh = Roo.DomHelper;
38358             if(c.titlebar !== false){
38359                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38360                 this.collapseBtn.on("click", this.collapse, this);
38361                 this.collapseBtn.enableDisplayMode();
38362                 /*
38363                 if(c.showPin === true || this.showPin){
38364                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38365                     this.stickBtn.enableDisplayMode();
38366                     this.stickBtn.on("click", this.expand, this);
38367                     this.stickBtn.hide();
38368                 }
38369                 
38370             }
38371             */
38372             /** This region's collapsed element
38373             * @type Roo.Element */
38374             /*
38375              *
38376             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38377                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38378             ]}, true);
38379             
38380             if(c.floatable !== false){
38381                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38382                this.collapsedEl.on("click", this.collapseClick, this);
38383             }
38384
38385             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38386                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38387                    id: "message", unselectable: "on", style:{"float":"left"}});
38388                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38389              }
38390             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38391             this.expandBtn.on("click", this.expand, this);
38392             
38393         }
38394         
38395         if(this.collapseBtn){
38396             this.collapseBtn.setVisible(c.collapsible == true);
38397         }
38398         
38399         this.cmargins = c.cmargins || this.cmargins ||
38400                          (this.position == "west" || this.position == "east" ?
38401                              {top: 0, left: 2, right:2, bottom: 0} :
38402                              {top: 2, left: 0, right:0, bottom: 2});
38403         */
38404         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38405         
38406         
38407         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38408         
38409         this.autoScroll = c.autoScroll || false;
38410         
38411         
38412        
38413         
38414         this.duration = c.duration || .30;
38415         this.slideDuration = c.slideDuration || .45;
38416         this.config = c;
38417        
38418     },
38419     /**
38420      * Returns true if this region is currently visible.
38421      * @return {Boolean}
38422      */
38423     isVisible : function(){
38424         return this.visible;
38425     },
38426
38427     /**
38428      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38429      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38430      */
38431     //setCollapsedTitle : function(title){
38432     //    title = title || "&#160;";
38433      //   if(this.collapsedTitleTextEl){
38434       //      this.collapsedTitleTextEl.innerHTML = title;
38435        // }
38436     //},
38437
38438     getBox : function(){
38439         var b;
38440       //  if(!this.collapsed){
38441             b = this.el.getBox(false, true);
38442        // }else{
38443           //  b = this.collapsedEl.getBox(false, true);
38444         //}
38445         return b;
38446     },
38447
38448     getMargins : function(){
38449         return this.margins;
38450         //return this.collapsed ? this.cmargins : this.margins;
38451     },
38452 /*
38453     highlight : function(){
38454         this.el.addClass("x-layout-panel-dragover");
38455     },
38456
38457     unhighlight : function(){
38458         this.el.removeClass("x-layout-panel-dragover");
38459     },
38460 */
38461     updateBox : function(box)
38462     {
38463         if (!this.bodyEl) {
38464             return; // not rendered yet..
38465         }
38466         
38467         this.box = box;
38468         if(!this.collapsed){
38469             this.el.dom.style.left = box.x + "px";
38470             this.el.dom.style.top = box.y + "px";
38471             this.updateBody(box.width, box.height);
38472         }else{
38473             this.collapsedEl.dom.style.left = box.x + "px";
38474             this.collapsedEl.dom.style.top = box.y + "px";
38475             this.collapsedEl.setSize(box.width, box.height);
38476         }
38477         if(this.tabs){
38478             this.tabs.autoSizeTabs();
38479         }
38480     },
38481
38482     updateBody : function(w, h)
38483     {
38484         if(w !== null){
38485             this.el.setWidth(w);
38486             w -= this.el.getBorderWidth("rl");
38487             if(this.config.adjustments){
38488                 w += this.config.adjustments[0];
38489             }
38490         }
38491         if(h !== null && h > 0){
38492             this.el.setHeight(h);
38493             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38494             h -= this.el.getBorderWidth("tb");
38495             if(this.config.adjustments){
38496                 h += this.config.adjustments[1];
38497             }
38498             this.bodyEl.setHeight(h);
38499             if(this.tabs){
38500                 h = this.tabs.syncHeight(h);
38501             }
38502         }
38503         if(this.panelSize){
38504             w = w !== null ? w : this.panelSize.width;
38505             h = h !== null ? h : this.panelSize.height;
38506         }
38507         if(this.activePanel){
38508             var el = this.activePanel.getEl();
38509             w = w !== null ? w : el.getWidth();
38510             h = h !== null ? h : el.getHeight();
38511             this.panelSize = {width: w, height: h};
38512             this.activePanel.setSize(w, h);
38513         }
38514         if(Roo.isIE && this.tabs){
38515             this.tabs.el.repaint();
38516         }
38517     },
38518
38519     /**
38520      * Returns the container element for this region.
38521      * @return {Roo.Element}
38522      */
38523     getEl : function(){
38524         return this.el;
38525     },
38526
38527     /**
38528      * Hides this region.
38529      */
38530     hide : function(){
38531         //if(!this.collapsed){
38532             this.el.dom.style.left = "-2000px";
38533             this.el.hide();
38534         //}else{
38535          //   this.collapsedEl.dom.style.left = "-2000px";
38536          //   this.collapsedEl.hide();
38537        // }
38538         this.visible = false;
38539         this.fireEvent("visibilitychange", this, false);
38540     },
38541
38542     /**
38543      * Shows this region if it was previously hidden.
38544      */
38545     show : function(){
38546         //if(!this.collapsed){
38547             this.el.show();
38548         //}else{
38549         //    this.collapsedEl.show();
38550        // }
38551         this.visible = true;
38552         this.fireEvent("visibilitychange", this, true);
38553     },
38554 /*
38555     closeClicked : function(){
38556         if(this.activePanel){
38557             this.remove(this.activePanel);
38558         }
38559     },
38560
38561     collapseClick : function(e){
38562         if(this.isSlid){
38563            e.stopPropagation();
38564            this.slideIn();
38565         }else{
38566            e.stopPropagation();
38567            this.slideOut();
38568         }
38569     },
38570 */
38571     /**
38572      * Collapses this region.
38573      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38574      */
38575     /*
38576     collapse : function(skipAnim, skipCheck = false){
38577         if(this.collapsed) {
38578             return;
38579         }
38580         
38581         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38582             
38583             this.collapsed = true;
38584             if(this.split){
38585                 this.split.el.hide();
38586             }
38587             if(this.config.animate && skipAnim !== true){
38588                 this.fireEvent("invalidated", this);
38589                 this.animateCollapse();
38590             }else{
38591                 this.el.setLocation(-20000,-20000);
38592                 this.el.hide();
38593                 this.collapsedEl.show();
38594                 this.fireEvent("collapsed", this);
38595                 this.fireEvent("invalidated", this);
38596             }
38597         }
38598         
38599     },
38600 */
38601     animateCollapse : function(){
38602         // overridden
38603     },
38604
38605     /**
38606      * Expands this region if it was previously collapsed.
38607      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38608      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38609      */
38610     /*
38611     expand : function(e, skipAnim){
38612         if(e) {
38613             e.stopPropagation();
38614         }
38615         if(!this.collapsed || this.el.hasActiveFx()) {
38616             return;
38617         }
38618         if(this.isSlid){
38619             this.afterSlideIn();
38620             skipAnim = true;
38621         }
38622         this.collapsed = false;
38623         if(this.config.animate && skipAnim !== true){
38624             this.animateExpand();
38625         }else{
38626             this.el.show();
38627             if(this.split){
38628                 this.split.el.show();
38629             }
38630             this.collapsedEl.setLocation(-2000,-2000);
38631             this.collapsedEl.hide();
38632             this.fireEvent("invalidated", this);
38633             this.fireEvent("expanded", this);
38634         }
38635     },
38636 */
38637     animateExpand : function(){
38638         // overridden
38639     },
38640
38641     initTabs : function()
38642     {
38643         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38644         
38645         var ts = new Roo.bootstrap.panel.Tabs({
38646             el: this.bodyEl.dom,
38647             region : this,
38648             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38649             disableTooltips: this.config.disableTabTips,
38650             toolbar : this.config.toolbar
38651         });
38652         
38653         if(this.config.hideTabs){
38654             ts.stripWrap.setDisplayed(false);
38655         }
38656         this.tabs = ts;
38657         ts.resizeTabs = this.config.resizeTabs === true;
38658         ts.minTabWidth = this.config.minTabWidth || 40;
38659         ts.maxTabWidth = this.config.maxTabWidth || 250;
38660         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38661         ts.monitorResize = false;
38662         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38663         ts.bodyEl.addClass('roo-layout-tabs-body');
38664         this.panels.each(this.initPanelAsTab, this);
38665     },
38666
38667     initPanelAsTab : function(panel){
38668         var ti = this.tabs.addTab(
38669             panel.getEl().id,
38670             panel.getTitle(),
38671             null,
38672             this.config.closeOnTab && panel.isClosable(),
38673             panel.tpl
38674         );
38675         if(panel.tabTip !== undefined){
38676             ti.setTooltip(panel.tabTip);
38677         }
38678         ti.on("activate", function(){
38679               this.setActivePanel(panel);
38680         }, this);
38681         
38682         if(this.config.closeOnTab){
38683             ti.on("beforeclose", function(t, e){
38684                 e.cancel = true;
38685                 this.remove(panel);
38686             }, this);
38687         }
38688         
38689         panel.tabItem = ti;
38690         
38691         return ti;
38692     },
38693
38694     updatePanelTitle : function(panel, title)
38695     {
38696         if(this.activePanel == panel){
38697             this.updateTitle(title);
38698         }
38699         if(this.tabs){
38700             var ti = this.tabs.getTab(panel.getEl().id);
38701             ti.setText(title);
38702             if(panel.tabTip !== undefined){
38703                 ti.setTooltip(panel.tabTip);
38704             }
38705         }
38706     },
38707
38708     updateTitle : function(title){
38709         if(this.titleTextEl && !this.config.title){
38710             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38711         }
38712     },
38713
38714     setActivePanel : function(panel)
38715     {
38716         panel = this.getPanel(panel);
38717         if(this.activePanel && this.activePanel != panel){
38718             if(this.activePanel.setActiveState(false) === false){
38719                 return;
38720             }
38721         }
38722         this.activePanel = panel;
38723         panel.setActiveState(true);
38724         if(this.panelSize){
38725             panel.setSize(this.panelSize.width, this.panelSize.height);
38726         }
38727         if(this.closeBtn){
38728             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38729         }
38730         this.updateTitle(panel.getTitle());
38731         if(this.tabs){
38732             this.fireEvent("invalidated", this);
38733         }
38734         this.fireEvent("panelactivated", this, panel);
38735     },
38736
38737     /**
38738      * Shows the specified panel.
38739      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38740      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38741      */
38742     showPanel : function(panel)
38743     {
38744         panel = this.getPanel(panel);
38745         if(panel){
38746             if(this.tabs){
38747                 var tab = this.tabs.getTab(panel.getEl().id);
38748                 if(tab.isHidden()){
38749                     this.tabs.unhideTab(tab.id);
38750                 }
38751                 tab.activate();
38752             }else{
38753                 this.setActivePanel(panel);
38754             }
38755         }
38756         return panel;
38757     },
38758
38759     /**
38760      * Get the active panel for this region.
38761      * @return {Roo.ContentPanel} The active panel or null
38762      */
38763     getActivePanel : function(){
38764         return this.activePanel;
38765     },
38766
38767     validateVisibility : function(){
38768         if(this.panels.getCount() < 1){
38769             this.updateTitle("&#160;");
38770             this.closeBtn.hide();
38771             this.hide();
38772         }else{
38773             if(!this.isVisible()){
38774                 this.show();
38775             }
38776         }
38777     },
38778
38779     /**
38780      * Adds the passed ContentPanel(s) to this region.
38781      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38782      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38783      */
38784     add : function(panel)
38785     {
38786         if(arguments.length > 1){
38787             for(var i = 0, len = arguments.length; i < len; i++) {
38788                 this.add(arguments[i]);
38789             }
38790             return null;
38791         }
38792         
38793         // if we have not been rendered yet, then we can not really do much of this..
38794         if (!this.bodyEl) {
38795             this.unrendered_panels.push(panel);
38796             return panel;
38797         }
38798         
38799         
38800         
38801         
38802         if(this.hasPanel(panel)){
38803             this.showPanel(panel);
38804             return panel;
38805         }
38806         panel.setRegion(this);
38807         this.panels.add(panel);
38808        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38809             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38810             // and hide them... ???
38811             this.bodyEl.dom.appendChild(panel.getEl().dom);
38812             if(panel.background !== true){
38813                 this.setActivePanel(panel);
38814             }
38815             this.fireEvent("paneladded", this, panel);
38816             return panel;
38817         }
38818         */
38819         if(!this.tabs){
38820             this.initTabs();
38821         }else{
38822             this.initPanelAsTab(panel);
38823         }
38824         
38825         
38826         if(panel.background !== true){
38827             this.tabs.activate(panel.getEl().id);
38828         }
38829         this.fireEvent("paneladded", this, panel);
38830         return panel;
38831     },
38832
38833     /**
38834      * Hides the tab for the specified panel.
38835      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38836      */
38837     hidePanel : function(panel){
38838         if(this.tabs && (panel = this.getPanel(panel))){
38839             this.tabs.hideTab(panel.getEl().id);
38840         }
38841     },
38842
38843     /**
38844      * Unhides the tab for a previously hidden panel.
38845      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38846      */
38847     unhidePanel : function(panel){
38848         if(this.tabs && (panel = this.getPanel(panel))){
38849             this.tabs.unhideTab(panel.getEl().id);
38850         }
38851     },
38852
38853     clearPanels : function(){
38854         while(this.panels.getCount() > 0){
38855              this.remove(this.panels.first());
38856         }
38857     },
38858
38859     /**
38860      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38861      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38862      * @param {Boolean} preservePanel Overrides the config preservePanel option
38863      * @return {Roo.ContentPanel} The panel that was removed
38864      */
38865     remove : function(panel, preservePanel)
38866     {
38867         panel = this.getPanel(panel);
38868         if(!panel){
38869             return null;
38870         }
38871         var e = {};
38872         this.fireEvent("beforeremove", this, panel, e);
38873         if(e.cancel === true){
38874             return null;
38875         }
38876         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38877         var panelId = panel.getId();
38878         this.panels.removeKey(panelId);
38879         if(preservePanel){
38880             document.body.appendChild(panel.getEl().dom);
38881         }
38882         if(this.tabs){
38883             this.tabs.removeTab(panel.getEl().id);
38884         }else if (!preservePanel){
38885             this.bodyEl.dom.removeChild(panel.getEl().dom);
38886         }
38887         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38888             var p = this.panels.first();
38889             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38890             tempEl.appendChild(p.getEl().dom);
38891             this.bodyEl.update("");
38892             this.bodyEl.dom.appendChild(p.getEl().dom);
38893             tempEl = null;
38894             this.updateTitle(p.getTitle());
38895             this.tabs = null;
38896             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38897             this.setActivePanel(p);
38898         }
38899         panel.setRegion(null);
38900         if(this.activePanel == panel){
38901             this.activePanel = null;
38902         }
38903         if(this.config.autoDestroy !== false && preservePanel !== true){
38904             try{panel.destroy();}catch(e){}
38905         }
38906         this.fireEvent("panelremoved", this, panel);
38907         return panel;
38908     },
38909
38910     /**
38911      * Returns the TabPanel component used by this region
38912      * @return {Roo.TabPanel}
38913      */
38914     getTabs : function(){
38915         return this.tabs;
38916     },
38917
38918     createTool : function(parentEl, className){
38919         var btn = Roo.DomHelper.append(parentEl, {
38920             tag: "div",
38921             cls: "x-layout-tools-button",
38922             children: [ {
38923                 tag: "div",
38924                 cls: "roo-layout-tools-button-inner " + className,
38925                 html: "&#160;"
38926             }]
38927         }, true);
38928         btn.addClassOnOver("roo-layout-tools-button-over");
38929         return btn;
38930     }
38931 });/*
38932  * Based on:
38933  * Ext JS Library 1.1.1
38934  * Copyright(c) 2006-2007, Ext JS, LLC.
38935  *
38936  * Originally Released Under LGPL - original licence link has changed is not relivant.
38937  *
38938  * Fork - LGPL
38939  * <script type="text/javascript">
38940  */
38941  
38942
38943
38944 /**
38945  * @class Roo.SplitLayoutRegion
38946  * @extends Roo.LayoutRegion
38947  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38948  */
38949 Roo.bootstrap.layout.Split = function(config){
38950     this.cursor = config.cursor;
38951     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38952 };
38953
38954 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38955 {
38956     splitTip : "Drag to resize.",
38957     collapsibleSplitTip : "Drag to resize. Double click to hide.",
38958     useSplitTips : false,
38959
38960     applyConfig : function(config){
38961         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38962     },
38963     
38964     onRender : function(ctr,pos) {
38965         
38966         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38967         if(!this.config.split){
38968             return;
38969         }
38970         if(!this.split){
38971             
38972             var splitEl = Roo.DomHelper.append(ctr.dom,  {
38973                             tag: "div",
38974                             id: this.el.id + "-split",
38975                             cls: "roo-layout-split roo-layout-split-"+this.position,
38976                             html: "&#160;"
38977             });
38978             /** The SplitBar for this region 
38979             * @type Roo.SplitBar */
38980             // does not exist yet...
38981             Roo.log([this.position, this.orientation]);
38982             
38983             this.split = new Roo.bootstrap.SplitBar({
38984                 dragElement : splitEl,
38985                 resizingElement: this.el,
38986                 orientation : this.orientation
38987             });
38988             
38989             this.split.on("moved", this.onSplitMove, this);
38990             this.split.useShim = this.config.useShim === true;
38991             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38992             if(this.useSplitTips){
38993                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38994             }
38995             //if(config.collapsible){
38996             //    this.split.el.on("dblclick", this.collapse,  this);
38997             //}
38998         }
38999         if(typeof this.config.minSize != "undefined"){
39000             this.split.minSize = this.config.minSize;
39001         }
39002         if(typeof this.config.maxSize != "undefined"){
39003             this.split.maxSize = this.config.maxSize;
39004         }
39005         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39006             this.hideSplitter();
39007         }
39008         
39009     },
39010
39011     getHMaxSize : function(){
39012          var cmax = this.config.maxSize || 10000;
39013          var center = this.mgr.getRegion("center");
39014          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39015     },
39016
39017     getVMaxSize : function(){
39018          var cmax = this.config.maxSize || 10000;
39019          var center = this.mgr.getRegion("center");
39020          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39021     },
39022
39023     onSplitMove : function(split, newSize){
39024         this.fireEvent("resized", this, newSize);
39025     },
39026     
39027     /** 
39028      * Returns the {@link Roo.SplitBar} for this region.
39029      * @return {Roo.SplitBar}
39030      */
39031     getSplitBar : function(){
39032         return this.split;
39033     },
39034     
39035     hide : function(){
39036         this.hideSplitter();
39037         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39038     },
39039
39040     hideSplitter : function(){
39041         if(this.split){
39042             this.split.el.setLocation(-2000,-2000);
39043             this.split.el.hide();
39044         }
39045     },
39046
39047     show : function(){
39048         if(this.split){
39049             this.split.el.show();
39050         }
39051         Roo.bootstrap.layout.Split.superclass.show.call(this);
39052     },
39053     
39054     beforeSlide: function(){
39055         if(Roo.isGecko){// firefox overflow auto bug workaround
39056             this.bodyEl.clip();
39057             if(this.tabs) {
39058                 this.tabs.bodyEl.clip();
39059             }
39060             if(this.activePanel){
39061                 this.activePanel.getEl().clip();
39062                 
39063                 if(this.activePanel.beforeSlide){
39064                     this.activePanel.beforeSlide();
39065                 }
39066             }
39067         }
39068     },
39069     
39070     afterSlide : function(){
39071         if(Roo.isGecko){// firefox overflow auto bug workaround
39072             this.bodyEl.unclip();
39073             if(this.tabs) {
39074                 this.tabs.bodyEl.unclip();
39075             }
39076             if(this.activePanel){
39077                 this.activePanel.getEl().unclip();
39078                 if(this.activePanel.afterSlide){
39079                     this.activePanel.afterSlide();
39080                 }
39081             }
39082         }
39083     },
39084
39085     initAutoHide : function(){
39086         if(this.autoHide !== false){
39087             if(!this.autoHideHd){
39088                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39089                 this.autoHideHd = {
39090                     "mouseout": function(e){
39091                         if(!e.within(this.el, true)){
39092                             st.delay(500);
39093                         }
39094                     },
39095                     "mouseover" : function(e){
39096                         st.cancel();
39097                     },
39098                     scope : this
39099                 };
39100             }
39101             this.el.on(this.autoHideHd);
39102         }
39103     },
39104
39105     clearAutoHide : function(){
39106         if(this.autoHide !== false){
39107             this.el.un("mouseout", this.autoHideHd.mouseout);
39108             this.el.un("mouseover", this.autoHideHd.mouseover);
39109         }
39110     },
39111
39112     clearMonitor : function(){
39113         Roo.get(document).un("click", this.slideInIf, this);
39114     },
39115
39116     // these names are backwards but not changed for compat
39117     slideOut : function(){
39118         if(this.isSlid || this.el.hasActiveFx()){
39119             return;
39120         }
39121         this.isSlid = true;
39122         if(this.collapseBtn){
39123             this.collapseBtn.hide();
39124         }
39125         this.closeBtnState = this.closeBtn.getStyle('display');
39126         this.closeBtn.hide();
39127         if(this.stickBtn){
39128             this.stickBtn.show();
39129         }
39130         this.el.show();
39131         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39132         this.beforeSlide();
39133         this.el.setStyle("z-index", 10001);
39134         this.el.slideIn(this.getSlideAnchor(), {
39135             callback: function(){
39136                 this.afterSlide();
39137                 this.initAutoHide();
39138                 Roo.get(document).on("click", this.slideInIf, this);
39139                 this.fireEvent("slideshow", this);
39140             },
39141             scope: this,
39142             block: true
39143         });
39144     },
39145
39146     afterSlideIn : function(){
39147         this.clearAutoHide();
39148         this.isSlid = false;
39149         this.clearMonitor();
39150         this.el.setStyle("z-index", "");
39151         if(this.collapseBtn){
39152             this.collapseBtn.show();
39153         }
39154         this.closeBtn.setStyle('display', this.closeBtnState);
39155         if(this.stickBtn){
39156             this.stickBtn.hide();
39157         }
39158         this.fireEvent("slidehide", this);
39159     },
39160
39161     slideIn : function(cb){
39162         if(!this.isSlid || this.el.hasActiveFx()){
39163             Roo.callback(cb);
39164             return;
39165         }
39166         this.isSlid = false;
39167         this.beforeSlide();
39168         this.el.slideOut(this.getSlideAnchor(), {
39169             callback: function(){
39170                 this.el.setLeftTop(-10000, -10000);
39171                 this.afterSlide();
39172                 this.afterSlideIn();
39173                 Roo.callback(cb);
39174             },
39175             scope: this,
39176             block: true
39177         });
39178     },
39179     
39180     slideInIf : function(e){
39181         if(!e.within(this.el)){
39182             this.slideIn();
39183         }
39184     },
39185
39186     animateCollapse : function(){
39187         this.beforeSlide();
39188         this.el.setStyle("z-index", 20000);
39189         var anchor = this.getSlideAnchor();
39190         this.el.slideOut(anchor, {
39191             callback : function(){
39192                 this.el.setStyle("z-index", "");
39193                 this.collapsedEl.slideIn(anchor, {duration:.3});
39194                 this.afterSlide();
39195                 this.el.setLocation(-10000,-10000);
39196                 this.el.hide();
39197                 this.fireEvent("collapsed", this);
39198             },
39199             scope: this,
39200             block: true
39201         });
39202     },
39203
39204     animateExpand : function(){
39205         this.beforeSlide();
39206         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39207         this.el.setStyle("z-index", 20000);
39208         this.collapsedEl.hide({
39209             duration:.1
39210         });
39211         this.el.slideIn(this.getSlideAnchor(), {
39212             callback : function(){
39213                 this.el.setStyle("z-index", "");
39214                 this.afterSlide();
39215                 if(this.split){
39216                     this.split.el.show();
39217                 }
39218                 this.fireEvent("invalidated", this);
39219                 this.fireEvent("expanded", this);
39220             },
39221             scope: this,
39222             block: true
39223         });
39224     },
39225
39226     anchors : {
39227         "west" : "left",
39228         "east" : "right",
39229         "north" : "top",
39230         "south" : "bottom"
39231     },
39232
39233     sanchors : {
39234         "west" : "l",
39235         "east" : "r",
39236         "north" : "t",
39237         "south" : "b"
39238     },
39239
39240     canchors : {
39241         "west" : "tl-tr",
39242         "east" : "tr-tl",
39243         "north" : "tl-bl",
39244         "south" : "bl-tl"
39245     },
39246
39247     getAnchor : function(){
39248         return this.anchors[this.position];
39249     },
39250
39251     getCollapseAnchor : function(){
39252         return this.canchors[this.position];
39253     },
39254
39255     getSlideAnchor : function(){
39256         return this.sanchors[this.position];
39257     },
39258
39259     getAlignAdj : function(){
39260         var cm = this.cmargins;
39261         switch(this.position){
39262             case "west":
39263                 return [0, 0];
39264             break;
39265             case "east":
39266                 return [0, 0];
39267             break;
39268             case "north":
39269                 return [0, 0];
39270             break;
39271             case "south":
39272                 return [0, 0];
39273             break;
39274         }
39275     },
39276
39277     getExpandAdj : function(){
39278         var c = this.collapsedEl, cm = this.cmargins;
39279         switch(this.position){
39280             case "west":
39281                 return [-(cm.right+c.getWidth()+cm.left), 0];
39282             break;
39283             case "east":
39284                 return [cm.right+c.getWidth()+cm.left, 0];
39285             break;
39286             case "north":
39287                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39288             break;
39289             case "south":
39290                 return [0, cm.top+cm.bottom+c.getHeight()];
39291             break;
39292         }
39293     }
39294 });/*
39295  * Based on:
39296  * Ext JS Library 1.1.1
39297  * Copyright(c) 2006-2007, Ext JS, LLC.
39298  *
39299  * Originally Released Under LGPL - original licence link has changed is not relivant.
39300  *
39301  * Fork - LGPL
39302  * <script type="text/javascript">
39303  */
39304 /*
39305  * These classes are private internal classes
39306  */
39307 Roo.bootstrap.layout.Center = function(config){
39308     config.region = "center";
39309     Roo.bootstrap.layout.Region.call(this, config);
39310     this.visible = true;
39311     this.minWidth = config.minWidth || 20;
39312     this.minHeight = config.minHeight || 20;
39313 };
39314
39315 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39316     hide : function(){
39317         // center panel can't be hidden
39318     },
39319     
39320     show : function(){
39321         // center panel can't be hidden
39322     },
39323     
39324     getMinWidth: function(){
39325         return this.minWidth;
39326     },
39327     
39328     getMinHeight: function(){
39329         return this.minHeight;
39330     }
39331 });
39332
39333
39334
39335
39336  
39337
39338
39339
39340
39341
39342
39343 Roo.bootstrap.layout.North = function(config)
39344 {
39345     config.region = 'north';
39346     config.cursor = 'n-resize';
39347     
39348     Roo.bootstrap.layout.Split.call(this, config);
39349     
39350     
39351     if(this.split){
39352         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39353         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39354         this.split.el.addClass("roo-layout-split-v");
39355     }
39356     //var size = config.initialSize || config.height;
39357     //if(this.el && typeof size != "undefined"){
39358     //    this.el.setHeight(size);
39359     //}
39360 };
39361 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39362 {
39363     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39364      
39365      
39366     onRender : function(ctr, pos)
39367     {
39368         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39369         var size = this.config.initialSize || this.config.height;
39370         if(this.el && typeof size != "undefined"){
39371             this.el.setHeight(size);
39372         }
39373     
39374     },
39375     
39376     getBox : function(){
39377         if(this.collapsed){
39378             return this.collapsedEl.getBox();
39379         }
39380         var box = this.el.getBox();
39381         if(this.split){
39382             box.height += this.split.el.getHeight();
39383         }
39384         return box;
39385     },
39386     
39387     updateBox : function(box){
39388         if(this.split && !this.collapsed){
39389             box.height -= this.split.el.getHeight();
39390             this.split.el.setLeft(box.x);
39391             this.split.el.setTop(box.y+box.height);
39392             this.split.el.setWidth(box.width);
39393         }
39394         if(this.collapsed){
39395             this.updateBody(box.width, null);
39396         }
39397         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39398     }
39399 });
39400
39401
39402
39403
39404
39405 Roo.bootstrap.layout.South = function(config){
39406     config.region = 'south';
39407     config.cursor = 's-resize';
39408     Roo.bootstrap.layout.Split.call(this, config);
39409     if(this.split){
39410         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39411         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39412         this.split.el.addClass("roo-layout-split-v");
39413     }
39414     
39415 };
39416
39417 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39418     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39419     
39420     onRender : function(ctr, pos)
39421     {
39422         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39423         var size = this.config.initialSize || this.config.height;
39424         if(this.el && typeof size != "undefined"){
39425             this.el.setHeight(size);
39426         }
39427     
39428     },
39429     
39430     getBox : function(){
39431         if(this.collapsed){
39432             return this.collapsedEl.getBox();
39433         }
39434         var box = this.el.getBox();
39435         if(this.split){
39436             var sh = this.split.el.getHeight();
39437             box.height += sh;
39438             box.y -= sh;
39439         }
39440         return box;
39441     },
39442     
39443     updateBox : function(box){
39444         if(this.split && !this.collapsed){
39445             var sh = this.split.el.getHeight();
39446             box.height -= sh;
39447             box.y += sh;
39448             this.split.el.setLeft(box.x);
39449             this.split.el.setTop(box.y-sh);
39450             this.split.el.setWidth(box.width);
39451         }
39452         if(this.collapsed){
39453             this.updateBody(box.width, null);
39454         }
39455         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39456     }
39457 });
39458
39459 Roo.bootstrap.layout.East = function(config){
39460     config.region = "east";
39461     config.cursor = "e-resize";
39462     Roo.bootstrap.layout.Split.call(this, config);
39463     if(this.split){
39464         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39465         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39466         this.split.el.addClass("roo-layout-split-h");
39467     }
39468     
39469 };
39470 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39471     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39472     
39473     onRender : function(ctr, pos)
39474     {
39475         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39476         var size = this.config.initialSize || this.config.width;
39477         if(this.el && typeof size != "undefined"){
39478             this.el.setWidth(size);
39479         }
39480     
39481     },
39482     
39483     getBox : function(){
39484         if(this.collapsed){
39485             return this.collapsedEl.getBox();
39486         }
39487         var box = this.el.getBox();
39488         if(this.split){
39489             var sw = this.split.el.getWidth();
39490             box.width += sw;
39491             box.x -= sw;
39492         }
39493         return box;
39494     },
39495
39496     updateBox : function(box){
39497         if(this.split && !this.collapsed){
39498             var sw = this.split.el.getWidth();
39499             box.width -= sw;
39500             this.split.el.setLeft(box.x);
39501             this.split.el.setTop(box.y);
39502             this.split.el.setHeight(box.height);
39503             box.x += sw;
39504         }
39505         if(this.collapsed){
39506             this.updateBody(null, box.height);
39507         }
39508         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39509     }
39510 });
39511
39512 Roo.bootstrap.layout.West = function(config){
39513     config.region = "west";
39514     config.cursor = "w-resize";
39515     
39516     Roo.bootstrap.layout.Split.call(this, config);
39517     if(this.split){
39518         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39519         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39520         this.split.el.addClass("roo-layout-split-h");
39521     }
39522     
39523 };
39524 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39525     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39526     
39527     onRender: function(ctr, pos)
39528     {
39529         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39530         var size = this.config.initialSize || this.config.width;
39531         if(typeof size != "undefined"){
39532             this.el.setWidth(size);
39533         }
39534     },
39535     
39536     getBox : function(){
39537         if(this.collapsed){
39538             return this.collapsedEl.getBox();
39539         }
39540         var box = this.el.getBox();
39541         if (box.width == 0) {
39542             box.width = this.config.width; // kludge?
39543         }
39544         if(this.split){
39545             box.width += this.split.el.getWidth();
39546         }
39547         return box;
39548     },
39549     
39550     updateBox : function(box){
39551         if(this.split && !this.collapsed){
39552             var sw = this.split.el.getWidth();
39553             box.width -= sw;
39554             this.split.el.setLeft(box.x+box.width);
39555             this.split.el.setTop(box.y);
39556             this.split.el.setHeight(box.height);
39557         }
39558         if(this.collapsed){
39559             this.updateBody(null, box.height);
39560         }
39561         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39562     }
39563 });Roo.namespace("Roo.bootstrap.panel");/*
39564  * Based on:
39565  * Ext JS Library 1.1.1
39566  * Copyright(c) 2006-2007, Ext JS, LLC.
39567  *
39568  * Originally Released Under LGPL - original licence link has changed is not relivant.
39569  *
39570  * Fork - LGPL
39571  * <script type="text/javascript">
39572  */
39573 /**
39574  * @class Roo.ContentPanel
39575  * @extends Roo.util.Observable
39576  * A basic ContentPanel element.
39577  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39578  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39579  * @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
39580  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39581  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39582  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39583  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39584  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39585  * @cfg {String} title          The title for this panel
39586  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39587  * @cfg {String} url            Calls {@link #setUrl} with this value
39588  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39589  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39590  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39591  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39592  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
39593  * @cfg {Boolean} badges render the badges
39594  * @cfg {String} cls  extra classes to use  
39595  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39596
39597  * @constructor
39598  * Create a new ContentPanel.
39599  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39600  * @param {String/Object} config A string to set only the title or a config object
39601  * @param {String} content (optional) Set the HTML content for this panel
39602  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39603  */
39604 Roo.bootstrap.panel.Content = function( config){
39605     
39606     this.tpl = config.tpl || false;
39607     
39608     var el = config.el;
39609     var content = config.content;
39610
39611     if(config.autoCreate){ // xtype is available if this is called from factory
39612         el = Roo.id();
39613     }
39614     this.el = Roo.get(el);
39615     if(!this.el && config && config.autoCreate){
39616         if(typeof config.autoCreate == "object"){
39617             if(!config.autoCreate.id){
39618                 config.autoCreate.id = config.id||el;
39619             }
39620             this.el = Roo.DomHelper.append(document.body,
39621                         config.autoCreate, true);
39622         }else{
39623             var elcfg =  {
39624                 tag: "div",
39625                 cls: (config.cls || '') +
39626                     (config.background ? ' bg-' + config.background : '') +
39627                     " roo-layout-inactive-content",
39628                 id: config.id||el
39629             };
39630             if (config.iframe) {
39631                 elcfg.cn = [
39632                     {
39633                         tag : 'iframe',
39634                         style : 'border: 0px',
39635                         src : 'about:blank'
39636                     }
39637                 ];
39638             }
39639               
39640             if (config.html) {
39641                 elcfg.html = config.html;
39642                 
39643             }
39644                         
39645             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39646             if (config.iframe) {
39647                 this.iframeEl = this.el.select('iframe',true).first();
39648             }
39649             
39650         }
39651     } 
39652     this.closable = false;
39653     this.loaded = false;
39654     this.active = false;
39655    
39656       
39657     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39658         
39659         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39660         
39661         this.wrapEl = this.el; //this.el.wrap();
39662         var ti = [];
39663         if (config.toolbar.items) {
39664             ti = config.toolbar.items ;
39665             delete config.toolbar.items ;
39666         }
39667         
39668         var nitems = [];
39669         this.toolbar.render(this.wrapEl, 'before');
39670         for(var i =0;i < ti.length;i++) {
39671           //  Roo.log(['add child', items[i]]);
39672             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39673         }
39674         this.toolbar.items = nitems;
39675         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39676         delete config.toolbar;
39677         
39678     }
39679     /*
39680     // xtype created footer. - not sure if will work as we normally have to render first..
39681     if (this.footer && !this.footer.el && this.footer.xtype) {
39682         if (!this.wrapEl) {
39683             this.wrapEl = this.el.wrap();
39684         }
39685     
39686         this.footer.container = this.wrapEl.createChild();
39687          
39688         this.footer = Roo.factory(this.footer, Roo);
39689         
39690     }
39691     */
39692     
39693      if(typeof config == "string"){
39694         this.title = config;
39695     }else{
39696         Roo.apply(this, config);
39697     }
39698     
39699     if(this.resizeEl){
39700         this.resizeEl = Roo.get(this.resizeEl, true);
39701     }else{
39702         this.resizeEl = this.el;
39703     }
39704     // handle view.xtype
39705     
39706  
39707     
39708     
39709     this.addEvents({
39710         /**
39711          * @event activate
39712          * Fires when this panel is activated. 
39713          * @param {Roo.ContentPanel} this
39714          */
39715         "activate" : true,
39716         /**
39717          * @event deactivate
39718          * Fires when this panel is activated. 
39719          * @param {Roo.ContentPanel} this
39720          */
39721         "deactivate" : true,
39722
39723         /**
39724          * @event resize
39725          * Fires when this panel is resized if fitToFrame is true.
39726          * @param {Roo.ContentPanel} this
39727          * @param {Number} width The width after any component adjustments
39728          * @param {Number} height The height after any component adjustments
39729          */
39730         "resize" : true,
39731         
39732          /**
39733          * @event render
39734          * Fires when this tab is created
39735          * @param {Roo.ContentPanel} this
39736          */
39737         "render" : true
39738         
39739         
39740         
39741     });
39742     
39743
39744     
39745     
39746     if(this.autoScroll && !this.iframe){
39747         this.resizeEl.setStyle("overflow", "auto");
39748     } else {
39749         // fix randome scrolling
39750         //this.el.on('scroll', function() {
39751         //    Roo.log('fix random scolling');
39752         //    this.scrollTo('top',0); 
39753         //});
39754     }
39755     content = content || this.content;
39756     if(content){
39757         this.setContent(content);
39758     }
39759     if(config && config.url){
39760         this.setUrl(this.url, this.params, this.loadOnce);
39761     }
39762     
39763     
39764     
39765     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39766     
39767     if (this.view && typeof(this.view.xtype) != 'undefined') {
39768         this.view.el = this.el.appendChild(document.createElement("div"));
39769         this.view = Roo.factory(this.view); 
39770         this.view.render  &&  this.view.render(false, '');  
39771     }
39772     
39773     
39774     this.fireEvent('render', this);
39775 };
39776
39777 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39778     
39779     cls : '',
39780     background : '',
39781     
39782     tabTip : '',
39783     
39784     iframe : false,
39785     iframeEl : false,
39786     
39787     setRegion : function(region){
39788         this.region = region;
39789         this.setActiveClass(region && !this.background);
39790     },
39791     
39792     
39793     setActiveClass: function(state)
39794     {
39795         if(state){
39796            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39797            this.el.setStyle('position','relative');
39798         }else{
39799            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39800            this.el.setStyle('position', 'absolute');
39801         } 
39802     },
39803     
39804     /**
39805      * Returns the toolbar for this Panel if one was configured. 
39806      * @return {Roo.Toolbar} 
39807      */
39808     getToolbar : function(){
39809         return this.toolbar;
39810     },
39811     
39812     setActiveState : function(active)
39813     {
39814         this.active = active;
39815         this.setActiveClass(active);
39816         if(!active){
39817             if(this.fireEvent("deactivate", this) === false){
39818                 return false;
39819             }
39820             return true;
39821         }
39822         this.fireEvent("activate", this);
39823         return true;
39824     },
39825     /**
39826      * Updates this panel's element (not for iframe)
39827      * @param {String} content The new content
39828      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39829     */
39830     setContent : function(content, loadScripts){
39831         if (this.iframe) {
39832             return;
39833         }
39834         
39835         this.el.update(content, loadScripts);
39836     },
39837
39838     ignoreResize : function(w, h){
39839         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39840             return true;
39841         }else{
39842             this.lastSize = {width: w, height: h};
39843             return false;
39844         }
39845     },
39846     /**
39847      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39848      * @return {Roo.UpdateManager} The UpdateManager
39849      */
39850     getUpdateManager : function(){
39851         if (this.iframe) {
39852             return false;
39853         }
39854         return this.el.getUpdateManager();
39855     },
39856      /**
39857      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39858      * Does not work with IFRAME contents
39859      * @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:
39860 <pre><code>
39861 panel.load({
39862     url: "your-url.php",
39863     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39864     callback: yourFunction,
39865     scope: yourObject, //(optional scope)
39866     discardUrl: false,
39867     nocache: false,
39868     text: "Loading...",
39869     timeout: 30,
39870     scripts: false
39871 });
39872 </code></pre>
39873      
39874      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39875      * 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.
39876      * @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}
39877      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39878      * @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.
39879      * @return {Roo.ContentPanel} this
39880      */
39881     load : function(){
39882         
39883         if (this.iframe) {
39884             return this;
39885         }
39886         
39887         var um = this.el.getUpdateManager();
39888         um.update.apply(um, arguments);
39889         return this;
39890     },
39891
39892
39893     /**
39894      * 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.
39895      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39896      * @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)
39897      * @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)
39898      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
39899      */
39900     setUrl : function(url, params, loadOnce){
39901         if (this.iframe) {
39902             this.iframeEl.dom.src = url;
39903             return false;
39904         }
39905         
39906         if(this.refreshDelegate){
39907             this.removeListener("activate", this.refreshDelegate);
39908         }
39909         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39910         this.on("activate", this.refreshDelegate);
39911         return this.el.getUpdateManager();
39912     },
39913     
39914     _handleRefresh : function(url, params, loadOnce){
39915         if(!loadOnce || !this.loaded){
39916             var updater = this.el.getUpdateManager();
39917             updater.update(url, params, this._setLoaded.createDelegate(this));
39918         }
39919     },
39920     
39921     _setLoaded : function(){
39922         this.loaded = true;
39923     }, 
39924     
39925     /**
39926      * Returns this panel's id
39927      * @return {String} 
39928      */
39929     getId : function(){
39930         return this.el.id;
39931     },
39932     
39933     /** 
39934      * Returns this panel's element - used by regiosn to add.
39935      * @return {Roo.Element} 
39936      */
39937     getEl : function(){
39938         return this.wrapEl || this.el;
39939     },
39940     
39941    
39942     
39943     adjustForComponents : function(width, height)
39944     {
39945         //Roo.log('adjustForComponents ');
39946         if(this.resizeEl != this.el){
39947             width -= this.el.getFrameWidth('lr');
39948             height -= this.el.getFrameWidth('tb');
39949         }
39950         if(this.toolbar){
39951             var te = this.toolbar.getEl();
39952             te.setWidth(width);
39953             height -= te.getHeight();
39954         }
39955         if(this.footer){
39956             var te = this.footer.getEl();
39957             te.setWidth(width);
39958             height -= te.getHeight();
39959         }
39960         
39961         
39962         if(this.adjustments){
39963             width += this.adjustments[0];
39964             height += this.adjustments[1];
39965         }
39966         return {"width": width, "height": height};
39967     },
39968     
39969     setSize : function(width, height){
39970         if(this.fitToFrame && !this.ignoreResize(width, height)){
39971             if(this.fitContainer && this.resizeEl != this.el){
39972                 this.el.setSize(width, height);
39973             }
39974             var size = this.adjustForComponents(width, height);
39975             if (this.iframe) {
39976                 this.iframeEl.setSize(width,height);
39977             }
39978             
39979             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39980             this.fireEvent('resize', this, size.width, size.height);
39981             
39982             
39983         }
39984     },
39985     
39986     /**
39987      * Returns this panel's title
39988      * @return {String} 
39989      */
39990     getTitle : function(){
39991         
39992         if (typeof(this.title) != 'object') {
39993             return this.title;
39994         }
39995         
39996         var t = '';
39997         for (var k in this.title) {
39998             if (!this.title.hasOwnProperty(k)) {
39999                 continue;
40000             }
40001             
40002             if (k.indexOf('-') >= 0) {
40003                 var s = k.split('-');
40004                 for (var i = 0; i<s.length; i++) {
40005                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40006                 }
40007             } else {
40008                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40009             }
40010         }
40011         return t;
40012     },
40013     
40014     /**
40015      * Set this panel's title
40016      * @param {String} title
40017      */
40018     setTitle : function(title){
40019         this.title = title;
40020         if(this.region){
40021             this.region.updatePanelTitle(this, title);
40022         }
40023     },
40024     
40025     /**
40026      * Returns true is this panel was configured to be closable
40027      * @return {Boolean} 
40028      */
40029     isClosable : function(){
40030         return this.closable;
40031     },
40032     
40033     beforeSlide : function(){
40034         this.el.clip();
40035         this.resizeEl.clip();
40036     },
40037     
40038     afterSlide : function(){
40039         this.el.unclip();
40040         this.resizeEl.unclip();
40041     },
40042     
40043     /**
40044      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40045      *   Will fail silently if the {@link #setUrl} method has not been called.
40046      *   This does not activate the panel, just updates its content.
40047      */
40048     refresh : function(){
40049         if(this.refreshDelegate){
40050            this.loaded = false;
40051            this.refreshDelegate();
40052         }
40053     },
40054     
40055     /**
40056      * Destroys this panel
40057      */
40058     destroy : function(){
40059         this.el.removeAllListeners();
40060         var tempEl = document.createElement("span");
40061         tempEl.appendChild(this.el.dom);
40062         tempEl.innerHTML = "";
40063         this.el.remove();
40064         this.el = null;
40065     },
40066     
40067     /**
40068      * form - if the content panel contains a form - this is a reference to it.
40069      * @type {Roo.form.Form}
40070      */
40071     form : false,
40072     /**
40073      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40074      *    This contains a reference to it.
40075      * @type {Roo.View}
40076      */
40077     view : false,
40078     
40079       /**
40080      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40081      * <pre><code>
40082
40083 layout.addxtype({
40084        xtype : 'Form',
40085        items: [ .... ]
40086    }
40087 );
40088
40089 </code></pre>
40090      * @param {Object} cfg Xtype definition of item to add.
40091      */
40092     
40093     
40094     getChildContainer: function () {
40095         return this.getEl();
40096     }
40097     
40098     
40099     /*
40100         var  ret = new Roo.factory(cfg);
40101         return ret;
40102         
40103         
40104         // add form..
40105         if (cfg.xtype.match(/^Form$/)) {
40106             
40107             var el;
40108             //if (this.footer) {
40109             //    el = this.footer.container.insertSibling(false, 'before');
40110             //} else {
40111                 el = this.el.createChild();
40112             //}
40113
40114             this.form = new  Roo.form.Form(cfg);
40115             
40116             
40117             if ( this.form.allItems.length) {
40118                 this.form.render(el.dom);
40119             }
40120             return this.form;
40121         }
40122         // should only have one of theses..
40123         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40124             // views.. should not be just added - used named prop 'view''
40125             
40126             cfg.el = this.el.appendChild(document.createElement("div"));
40127             // factory?
40128             
40129             var ret = new Roo.factory(cfg);
40130              
40131              ret.render && ret.render(false, ''); // render blank..
40132             this.view = ret;
40133             return ret;
40134         }
40135         return false;
40136     }
40137     \*/
40138 });
40139  
40140 /**
40141  * @class Roo.bootstrap.panel.Grid
40142  * @extends Roo.bootstrap.panel.Content
40143  * @constructor
40144  * Create a new GridPanel.
40145  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40146  * @param {Object} config A the config object
40147   
40148  */
40149
40150
40151
40152 Roo.bootstrap.panel.Grid = function(config)
40153 {
40154     
40155       
40156     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40157         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40158
40159     config.el = this.wrapper;
40160     //this.el = this.wrapper;
40161     
40162       if (config.container) {
40163         // ctor'ed from a Border/panel.grid
40164         
40165         
40166         this.wrapper.setStyle("overflow", "hidden");
40167         this.wrapper.addClass('roo-grid-container');
40168
40169     }
40170     
40171     
40172     if(config.toolbar){
40173         var tool_el = this.wrapper.createChild();    
40174         this.toolbar = Roo.factory(config.toolbar);
40175         var ti = [];
40176         if (config.toolbar.items) {
40177             ti = config.toolbar.items ;
40178             delete config.toolbar.items ;
40179         }
40180         
40181         var nitems = [];
40182         this.toolbar.render(tool_el);
40183         for(var i =0;i < ti.length;i++) {
40184           //  Roo.log(['add child', items[i]]);
40185             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40186         }
40187         this.toolbar.items = nitems;
40188         
40189         delete config.toolbar;
40190     }
40191     
40192     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40193     config.grid.scrollBody = true;;
40194     config.grid.monitorWindowResize = false; // turn off autosizing
40195     config.grid.autoHeight = false;
40196     config.grid.autoWidth = false;
40197     
40198     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40199     
40200     if (config.background) {
40201         // render grid on panel activation (if panel background)
40202         this.on('activate', function(gp) {
40203             if (!gp.grid.rendered) {
40204                 gp.grid.render(this.wrapper);
40205                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40206             }
40207         });
40208             
40209     } else {
40210         this.grid.render(this.wrapper);
40211         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40212
40213     }
40214     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40215     // ??? needed ??? config.el = this.wrapper;
40216     
40217     
40218     
40219   
40220     // xtype created footer. - not sure if will work as we normally have to render first..
40221     if (this.footer && !this.footer.el && this.footer.xtype) {
40222         
40223         var ctr = this.grid.getView().getFooterPanel(true);
40224         this.footer.dataSource = this.grid.dataSource;
40225         this.footer = Roo.factory(this.footer, Roo);
40226         this.footer.render(ctr);
40227         
40228     }
40229     
40230     
40231     
40232     
40233      
40234 };
40235
40236 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40237     getId : function(){
40238         return this.grid.id;
40239     },
40240     
40241     /**
40242      * Returns the grid for this panel
40243      * @return {Roo.bootstrap.Table} 
40244      */
40245     getGrid : function(){
40246         return this.grid;    
40247     },
40248     
40249     setSize : function(width, height){
40250         if(!this.ignoreResize(width, height)){
40251             var grid = this.grid;
40252             var size = this.adjustForComponents(width, height);
40253             // tfoot is not a footer?
40254           
40255             
40256             var gridel = grid.getGridEl();
40257             gridel.setSize(size.width, size.height);
40258             
40259             var tbd = grid.getGridEl().select('tbody', true).first();
40260             var thd = grid.getGridEl().select('thead',true).first();
40261             var tbf= grid.getGridEl().select('tfoot', true).first();
40262
40263             if (tbf) {
40264                 size.height -= tbf.getHeight();
40265             }
40266             if (thd) {
40267                 size.height -= thd.getHeight();
40268             }
40269             
40270             tbd.setSize(size.width, size.height );
40271             // this is for the account management tab -seems to work there.
40272             var thd = grid.getGridEl().select('thead',true).first();
40273             //if (tbd) {
40274             //    tbd.setSize(size.width, size.height - thd.getHeight());
40275             //}
40276              
40277             grid.autoSize();
40278         }
40279     },
40280      
40281     
40282     
40283     beforeSlide : function(){
40284         this.grid.getView().scroller.clip();
40285     },
40286     
40287     afterSlide : function(){
40288         this.grid.getView().scroller.unclip();
40289     },
40290     
40291     destroy : function(){
40292         this.grid.destroy();
40293         delete this.grid;
40294         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40295     }
40296 });
40297
40298 /**
40299  * @class Roo.bootstrap.panel.Nest
40300  * @extends Roo.bootstrap.panel.Content
40301  * @constructor
40302  * Create a new Panel, that can contain a layout.Border.
40303  * 
40304  * 
40305  * @param {Roo.BorderLayout} layout The layout for this panel
40306  * @param {String/Object} config A string to set only the title or a config object
40307  */
40308 Roo.bootstrap.panel.Nest = function(config)
40309 {
40310     // construct with only one argument..
40311     /* FIXME - implement nicer consturctors
40312     if (layout.layout) {
40313         config = layout;
40314         layout = config.layout;
40315         delete config.layout;
40316     }
40317     if (layout.xtype && !layout.getEl) {
40318         // then layout needs constructing..
40319         layout = Roo.factory(layout, Roo);
40320     }
40321     */
40322     
40323     config.el =  config.layout.getEl();
40324     
40325     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40326     
40327     config.layout.monitorWindowResize = false; // turn off autosizing
40328     this.layout = config.layout;
40329     this.layout.getEl().addClass("roo-layout-nested-layout");
40330     this.layout.parent = this;
40331     
40332     
40333     
40334     
40335 };
40336
40337 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40338
40339     setSize : function(width, height){
40340         if(!this.ignoreResize(width, height)){
40341             var size = this.adjustForComponents(width, height);
40342             var el = this.layout.getEl();
40343             if (size.height < 1) {
40344                 el.setWidth(size.width);   
40345             } else {
40346                 el.setSize(size.width, size.height);
40347             }
40348             var touch = el.dom.offsetWidth;
40349             this.layout.layout();
40350             // ie requires a double layout on the first pass
40351             if(Roo.isIE && !this.initialized){
40352                 this.initialized = true;
40353                 this.layout.layout();
40354             }
40355         }
40356     },
40357     
40358     // activate all subpanels if not currently active..
40359     
40360     setActiveState : function(active){
40361         this.active = active;
40362         this.setActiveClass(active);
40363         
40364         if(!active){
40365             this.fireEvent("deactivate", this);
40366             return;
40367         }
40368         
40369         this.fireEvent("activate", this);
40370         // not sure if this should happen before or after..
40371         if (!this.layout) {
40372             return; // should not happen..
40373         }
40374         var reg = false;
40375         for (var r in this.layout.regions) {
40376             reg = this.layout.getRegion(r);
40377             if (reg.getActivePanel()) {
40378                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40379                 reg.setActivePanel(reg.getActivePanel());
40380                 continue;
40381             }
40382             if (!reg.panels.length) {
40383                 continue;
40384             }
40385             reg.showPanel(reg.getPanel(0));
40386         }
40387         
40388         
40389         
40390         
40391     },
40392     
40393     /**
40394      * Returns the nested BorderLayout for this panel
40395      * @return {Roo.BorderLayout} 
40396      */
40397     getLayout : function(){
40398         return this.layout;
40399     },
40400     
40401      /**
40402      * Adds a xtype elements to the layout of the nested panel
40403      * <pre><code>
40404
40405 panel.addxtype({
40406        xtype : 'ContentPanel',
40407        region: 'west',
40408        items: [ .... ]
40409    }
40410 );
40411
40412 panel.addxtype({
40413         xtype : 'NestedLayoutPanel',
40414         region: 'west',
40415         layout: {
40416            center: { },
40417            west: { }   
40418         },
40419         items : [ ... list of content panels or nested layout panels.. ]
40420    }
40421 );
40422 </code></pre>
40423      * @param {Object} cfg Xtype definition of item to add.
40424      */
40425     addxtype : function(cfg) {
40426         return this.layout.addxtype(cfg);
40427     
40428     }
40429 });/*
40430  * Based on:
40431  * Ext JS Library 1.1.1
40432  * Copyright(c) 2006-2007, Ext JS, LLC.
40433  *
40434  * Originally Released Under LGPL - original licence link has changed is not relivant.
40435  *
40436  * Fork - LGPL
40437  * <script type="text/javascript">
40438  */
40439 /**
40440  * @class Roo.TabPanel
40441  * @extends Roo.util.Observable
40442  * A lightweight tab container.
40443  * <br><br>
40444  * Usage:
40445  * <pre><code>
40446 // basic tabs 1, built from existing content
40447 var tabs = new Roo.TabPanel("tabs1");
40448 tabs.addTab("script", "View Script");
40449 tabs.addTab("markup", "View Markup");
40450 tabs.activate("script");
40451
40452 // more advanced tabs, built from javascript
40453 var jtabs = new Roo.TabPanel("jtabs");
40454 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40455
40456 // set up the UpdateManager
40457 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40458 var updater = tab2.getUpdateManager();
40459 updater.setDefaultUrl("ajax1.htm");
40460 tab2.on('activate', updater.refresh, updater, true);
40461
40462 // Use setUrl for Ajax loading
40463 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40464 tab3.setUrl("ajax2.htm", null, true);
40465
40466 // Disabled tab
40467 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40468 tab4.disable();
40469
40470 jtabs.activate("jtabs-1");
40471  * </code></pre>
40472  * @constructor
40473  * Create a new TabPanel.
40474  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40475  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40476  */
40477 Roo.bootstrap.panel.Tabs = function(config){
40478     /**
40479     * The container element for this TabPanel.
40480     * @type Roo.Element
40481     */
40482     this.el = Roo.get(config.el);
40483     delete config.el;
40484     if(config){
40485         if(typeof config == "boolean"){
40486             this.tabPosition = config ? "bottom" : "top";
40487         }else{
40488             Roo.apply(this, config);
40489         }
40490     }
40491     
40492     if(this.tabPosition == "bottom"){
40493         // if tabs are at the bottom = create the body first.
40494         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40495         this.el.addClass("roo-tabs-bottom");
40496     }
40497     // next create the tabs holders
40498     
40499     if (this.tabPosition == "west"){
40500         
40501         var reg = this.region; // fake it..
40502         while (reg) {
40503             if (!reg.mgr.parent) {
40504                 break;
40505             }
40506             reg = reg.mgr.parent.region;
40507         }
40508         Roo.log("got nest?");
40509         Roo.log(reg);
40510         if (reg.mgr.getRegion('west')) {
40511             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40512             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40513             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40514             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40515             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40516         
40517             
40518         }
40519         
40520         
40521     } else {
40522      
40523         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40524         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40525         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40526         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40527     }
40528     
40529     
40530     if(Roo.isIE){
40531         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40532     }
40533     
40534     // finally - if tabs are at the top, then create the body last..
40535     if(this.tabPosition != "bottom"){
40536         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40537          * @type Roo.Element
40538          */
40539         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40540         this.el.addClass("roo-tabs-top");
40541     }
40542     this.items = [];
40543
40544     this.bodyEl.setStyle("position", "relative");
40545
40546     this.active = null;
40547     this.activateDelegate = this.activate.createDelegate(this);
40548
40549     this.addEvents({
40550         /**
40551          * @event tabchange
40552          * Fires when the active tab changes
40553          * @param {Roo.TabPanel} this
40554          * @param {Roo.TabPanelItem} activePanel The new active tab
40555          */
40556         "tabchange": true,
40557         /**
40558          * @event beforetabchange
40559          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40560          * @param {Roo.TabPanel} this
40561          * @param {Object} e Set cancel to true on this object to cancel the tab change
40562          * @param {Roo.TabPanelItem} tab The tab being changed to
40563          */
40564         "beforetabchange" : true
40565     });
40566
40567     Roo.EventManager.onWindowResize(this.onResize, this);
40568     this.cpad = this.el.getPadding("lr");
40569     this.hiddenCount = 0;
40570
40571
40572     // toolbar on the tabbar support...
40573     if (this.toolbar) {
40574         alert("no toolbar support yet");
40575         this.toolbar  = false;
40576         /*
40577         var tcfg = this.toolbar;
40578         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40579         this.toolbar = new Roo.Toolbar(tcfg);
40580         if (Roo.isSafari) {
40581             var tbl = tcfg.container.child('table', true);
40582             tbl.setAttribute('width', '100%');
40583         }
40584         */
40585         
40586     }
40587    
40588
40589
40590     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40591 };
40592
40593 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40594     /*
40595      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40596      */
40597     tabPosition : "top",
40598     /*
40599      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40600      */
40601     currentTabWidth : 0,
40602     /*
40603      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40604      */
40605     minTabWidth : 40,
40606     /*
40607      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40608      */
40609     maxTabWidth : 250,
40610     /*
40611      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40612      */
40613     preferredTabWidth : 175,
40614     /*
40615      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40616      */
40617     resizeTabs : false,
40618     /*
40619      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40620      */
40621     monitorResize : true,
40622     /*
40623      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40624      */
40625     toolbar : false,  // set by caller..
40626     
40627     region : false, /// set by caller
40628     
40629     disableTooltips : true, // not used yet...
40630
40631     /**
40632      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40633      * @param {String} id The id of the div to use <b>or create</b>
40634      * @param {String} text The text for the tab
40635      * @param {String} content (optional) Content to put in the TabPanelItem body
40636      * @param {Boolean} closable (optional) True to create a close icon on the tab
40637      * @return {Roo.TabPanelItem} The created TabPanelItem
40638      */
40639     addTab : function(id, text, content, closable, tpl)
40640     {
40641         var item = new Roo.bootstrap.panel.TabItem({
40642             panel: this,
40643             id : id,
40644             text : text,
40645             closable : closable,
40646             tpl : tpl
40647         });
40648         this.addTabItem(item);
40649         if(content){
40650             item.setContent(content);
40651         }
40652         return item;
40653     },
40654
40655     /**
40656      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40657      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40658      * @return {Roo.TabPanelItem}
40659      */
40660     getTab : function(id){
40661         return this.items[id];
40662     },
40663
40664     /**
40665      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40666      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40667      */
40668     hideTab : function(id){
40669         var t = this.items[id];
40670         if(!t.isHidden()){
40671            t.setHidden(true);
40672            this.hiddenCount++;
40673            this.autoSizeTabs();
40674         }
40675     },
40676
40677     /**
40678      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40679      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40680      */
40681     unhideTab : function(id){
40682         var t = this.items[id];
40683         if(t.isHidden()){
40684            t.setHidden(false);
40685            this.hiddenCount--;
40686            this.autoSizeTabs();
40687         }
40688     },
40689
40690     /**
40691      * Adds an existing {@link Roo.TabPanelItem}.
40692      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40693      */
40694     addTabItem : function(item)
40695     {
40696         this.items[item.id] = item;
40697         this.items.push(item);
40698         this.autoSizeTabs();
40699       //  if(this.resizeTabs){
40700     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40701   //         this.autoSizeTabs();
40702 //        }else{
40703 //            item.autoSize();
40704        // }
40705     },
40706
40707     /**
40708      * Removes a {@link Roo.TabPanelItem}.
40709      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40710      */
40711     removeTab : function(id){
40712         var items = this.items;
40713         var tab = items[id];
40714         if(!tab) { return; }
40715         var index = items.indexOf(tab);
40716         if(this.active == tab && items.length > 1){
40717             var newTab = this.getNextAvailable(index);
40718             if(newTab) {
40719                 newTab.activate();
40720             }
40721         }
40722         this.stripEl.dom.removeChild(tab.pnode.dom);
40723         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40724             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40725         }
40726         items.splice(index, 1);
40727         delete this.items[tab.id];
40728         tab.fireEvent("close", tab);
40729         tab.purgeListeners();
40730         this.autoSizeTabs();
40731     },
40732
40733     getNextAvailable : function(start){
40734         var items = this.items;
40735         var index = start;
40736         // look for a next tab that will slide over to
40737         // replace the one being removed
40738         while(index < items.length){
40739             var item = items[++index];
40740             if(item && !item.isHidden()){
40741                 return item;
40742             }
40743         }
40744         // if one isn't found select the previous tab (on the left)
40745         index = start;
40746         while(index >= 0){
40747             var item = items[--index];
40748             if(item && !item.isHidden()){
40749                 return item;
40750             }
40751         }
40752         return null;
40753     },
40754
40755     /**
40756      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40757      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40758      */
40759     disableTab : function(id){
40760         var tab = this.items[id];
40761         if(tab && this.active != tab){
40762             tab.disable();
40763         }
40764     },
40765
40766     /**
40767      * Enables a {@link Roo.TabPanelItem} that is disabled.
40768      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40769      */
40770     enableTab : function(id){
40771         var tab = this.items[id];
40772         tab.enable();
40773     },
40774
40775     /**
40776      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40777      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40778      * @return {Roo.TabPanelItem} The TabPanelItem.
40779      */
40780     activate : function(id)
40781     {
40782         //Roo.log('activite:'  + id);
40783         
40784         var tab = this.items[id];
40785         if(!tab){
40786             return null;
40787         }
40788         if(tab == this.active || tab.disabled){
40789             return tab;
40790         }
40791         var e = {};
40792         this.fireEvent("beforetabchange", this, e, tab);
40793         if(e.cancel !== true && !tab.disabled){
40794             if(this.active){
40795                 this.active.hide();
40796             }
40797             this.active = this.items[id];
40798             this.active.show();
40799             this.fireEvent("tabchange", this, this.active);
40800         }
40801         return tab;
40802     },
40803
40804     /**
40805      * Gets the active {@link Roo.TabPanelItem}.
40806      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40807      */
40808     getActiveTab : function(){
40809         return this.active;
40810     },
40811
40812     /**
40813      * Updates the tab body element to fit the height of the container element
40814      * for overflow scrolling
40815      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40816      */
40817     syncHeight : function(targetHeight){
40818         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40819         var bm = this.bodyEl.getMargins();
40820         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40821         this.bodyEl.setHeight(newHeight);
40822         return newHeight;
40823     },
40824
40825     onResize : function(){
40826         if(this.monitorResize){
40827             this.autoSizeTabs();
40828         }
40829     },
40830
40831     /**
40832      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40833      */
40834     beginUpdate : function(){
40835         this.updating = true;
40836     },
40837
40838     /**
40839      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40840      */
40841     endUpdate : function(){
40842         this.updating = false;
40843         this.autoSizeTabs();
40844     },
40845
40846     /**
40847      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40848      */
40849     autoSizeTabs : function()
40850     {
40851         var count = this.items.length;
40852         var vcount = count - this.hiddenCount;
40853         
40854         if (vcount < 2) {
40855             this.stripEl.hide();
40856         } else {
40857             this.stripEl.show();
40858         }
40859         
40860         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40861             return;
40862         }
40863         
40864         
40865         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40866         var availWidth = Math.floor(w / vcount);
40867         var b = this.stripBody;
40868         if(b.getWidth() > w){
40869             var tabs = this.items;
40870             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40871             if(availWidth < this.minTabWidth){
40872                 /*if(!this.sleft){    // incomplete scrolling code
40873                     this.createScrollButtons();
40874                 }
40875                 this.showScroll();
40876                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40877             }
40878         }else{
40879             if(this.currentTabWidth < this.preferredTabWidth){
40880                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40881             }
40882         }
40883     },
40884
40885     /**
40886      * Returns the number of tabs in this TabPanel.
40887      * @return {Number}
40888      */
40889      getCount : function(){
40890          return this.items.length;
40891      },
40892
40893     /**
40894      * Resizes all the tabs to the passed width
40895      * @param {Number} The new width
40896      */
40897     setTabWidth : function(width){
40898         this.currentTabWidth = width;
40899         for(var i = 0, len = this.items.length; i < len; i++) {
40900                 if(!this.items[i].isHidden()) {
40901                 this.items[i].setWidth(width);
40902             }
40903         }
40904     },
40905
40906     /**
40907      * Destroys this TabPanel
40908      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40909      */
40910     destroy : function(removeEl){
40911         Roo.EventManager.removeResizeListener(this.onResize, this);
40912         for(var i = 0, len = this.items.length; i < len; i++){
40913             this.items[i].purgeListeners();
40914         }
40915         if(removeEl === true){
40916             this.el.update("");
40917             this.el.remove();
40918         }
40919     },
40920     
40921     createStrip : function(container)
40922     {
40923         var strip = document.createElement("nav");
40924         strip.className = Roo.bootstrap.version == 4 ?
40925             "navbar-light bg-light" : 
40926             "navbar navbar-default"; //"x-tabs-wrap";
40927         container.appendChild(strip);
40928         return strip;
40929     },
40930     
40931     createStripList : function(strip)
40932     {
40933         // div wrapper for retard IE
40934         // returns the "tr" element.
40935         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40936         //'<div class="x-tabs-strip-wrap">'+
40937           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40938           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40939         return strip.firstChild; //.firstChild.firstChild.firstChild;
40940     },
40941     createBody : function(container)
40942     {
40943         var body = document.createElement("div");
40944         Roo.id(body, "tab-body");
40945         //Roo.fly(body).addClass("x-tabs-body");
40946         Roo.fly(body).addClass("tab-content");
40947         container.appendChild(body);
40948         return body;
40949     },
40950     createItemBody :function(bodyEl, id){
40951         var body = Roo.getDom(id);
40952         if(!body){
40953             body = document.createElement("div");
40954             body.id = id;
40955         }
40956         //Roo.fly(body).addClass("x-tabs-item-body");
40957         Roo.fly(body).addClass("tab-pane");
40958          bodyEl.insertBefore(body, bodyEl.firstChild);
40959         return body;
40960     },
40961     /** @private */
40962     createStripElements :  function(stripEl, text, closable, tpl)
40963     {
40964         var td = document.createElement("li"); // was td..
40965         td.className = 'nav-item';
40966         
40967         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40968         
40969         
40970         stripEl.appendChild(td);
40971         /*if(closable){
40972             td.className = "x-tabs-closable";
40973             if(!this.closeTpl){
40974                 this.closeTpl = new Roo.Template(
40975                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40976                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40977                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
40978                 );
40979             }
40980             var el = this.closeTpl.overwrite(td, {"text": text});
40981             var close = el.getElementsByTagName("div")[0];
40982             var inner = el.getElementsByTagName("em")[0];
40983             return {"el": el, "close": close, "inner": inner};
40984         } else {
40985         */
40986         // not sure what this is..
40987 //            if(!this.tabTpl){
40988                 //this.tabTpl = new Roo.Template(
40989                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40990                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40991                 //);
40992 //                this.tabTpl = new Roo.Template(
40993 //                   '<a href="#">' +
40994 //                   '<span unselectable="on"' +
40995 //                            (this.disableTooltips ? '' : ' title="{text}"') +
40996 //                            ' >{text}</span></a>'
40997 //                );
40998 //                
40999 //            }
41000
41001
41002             var template = tpl || this.tabTpl || false;
41003             
41004             if(!template){
41005                 template =  new Roo.Template(
41006                         Roo.bootstrap.version == 4 ? 
41007                             (
41008                                 '<a class="nav-link" href="#" unselectable="on"' +
41009                                      (this.disableTooltips ? '' : ' title="{text}"') +
41010                                      ' >{text}</a>'
41011                             ) : (
41012                                 '<a class="nav-link" href="#">' +
41013                                 '<span unselectable="on"' +
41014                                          (this.disableTooltips ? '' : ' title="{text}"') +
41015                                     ' >{text}</span></a>'
41016                             )
41017                 );
41018             }
41019             
41020             switch (typeof(template)) {
41021                 case 'object' :
41022                     break;
41023                 case 'string' :
41024                     template = new Roo.Template(template);
41025                     break;
41026                 default :
41027                     break;
41028             }
41029             
41030             var el = template.overwrite(td, {"text": text});
41031             
41032             var inner = el.getElementsByTagName("span")[0];
41033             
41034             return {"el": el, "inner": inner};
41035             
41036     }
41037         
41038     
41039 });
41040
41041 /**
41042  * @class Roo.TabPanelItem
41043  * @extends Roo.util.Observable
41044  * Represents an individual item (tab plus body) in a TabPanel.
41045  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41046  * @param {String} id The id of this TabPanelItem
41047  * @param {String} text The text for the tab of this TabPanelItem
41048  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41049  */
41050 Roo.bootstrap.panel.TabItem = function(config){
41051     /**
41052      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41053      * @type Roo.TabPanel
41054      */
41055     this.tabPanel = config.panel;
41056     /**
41057      * The id for this TabPanelItem
41058      * @type String
41059      */
41060     this.id = config.id;
41061     /** @private */
41062     this.disabled = false;
41063     /** @private */
41064     this.text = config.text;
41065     /** @private */
41066     this.loaded = false;
41067     this.closable = config.closable;
41068
41069     /**
41070      * The body element for this TabPanelItem.
41071      * @type Roo.Element
41072      */
41073     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41074     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41075     this.bodyEl.setStyle("display", "block");
41076     this.bodyEl.setStyle("zoom", "1");
41077     //this.hideAction();
41078
41079     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41080     /** @private */
41081     this.el = Roo.get(els.el);
41082     this.inner = Roo.get(els.inner, true);
41083      this.textEl = Roo.bootstrap.version == 4 ?
41084         this.el : Roo.get(this.el.dom.firstChild, true);
41085
41086     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41087     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41088
41089     
41090 //    this.el.on("mousedown", this.onTabMouseDown, this);
41091     this.el.on("click", this.onTabClick, this);
41092     /** @private */
41093     if(config.closable){
41094         var c = Roo.get(els.close, true);
41095         c.dom.title = this.closeText;
41096         c.addClassOnOver("close-over");
41097         c.on("click", this.closeClick, this);
41098      }
41099
41100     this.addEvents({
41101          /**
41102          * @event activate
41103          * Fires when this tab becomes the active tab.
41104          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41105          * @param {Roo.TabPanelItem} this
41106          */
41107         "activate": true,
41108         /**
41109          * @event beforeclose
41110          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41111          * @param {Roo.TabPanelItem} this
41112          * @param {Object} e Set cancel to true on this object to cancel the close.
41113          */
41114         "beforeclose": true,
41115         /**
41116          * @event close
41117          * Fires when this tab is closed.
41118          * @param {Roo.TabPanelItem} this
41119          */
41120          "close": true,
41121         /**
41122          * @event deactivate
41123          * Fires when this tab is no longer the active tab.
41124          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41125          * @param {Roo.TabPanelItem} this
41126          */
41127          "deactivate" : true
41128     });
41129     this.hidden = false;
41130
41131     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41132 };
41133
41134 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41135            {
41136     purgeListeners : function(){
41137        Roo.util.Observable.prototype.purgeListeners.call(this);
41138        this.el.removeAllListeners();
41139     },
41140     /**
41141      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41142      */
41143     show : function(){
41144         this.status_node.addClass("active");
41145         this.showAction();
41146         if(Roo.isOpera){
41147             this.tabPanel.stripWrap.repaint();
41148         }
41149         this.fireEvent("activate", this.tabPanel, this);
41150     },
41151
41152     /**
41153      * Returns true if this tab is the active tab.
41154      * @return {Boolean}
41155      */
41156     isActive : function(){
41157         return this.tabPanel.getActiveTab() == this;
41158     },
41159
41160     /**
41161      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41162      */
41163     hide : function(){
41164         this.status_node.removeClass("active");
41165         this.hideAction();
41166         this.fireEvent("deactivate", this.tabPanel, this);
41167     },
41168
41169     hideAction : function(){
41170         this.bodyEl.hide();
41171         this.bodyEl.setStyle("position", "absolute");
41172         this.bodyEl.setLeft("-20000px");
41173         this.bodyEl.setTop("-20000px");
41174     },
41175
41176     showAction : function(){
41177         this.bodyEl.setStyle("position", "relative");
41178         this.bodyEl.setTop("");
41179         this.bodyEl.setLeft("");
41180         this.bodyEl.show();
41181     },
41182
41183     /**
41184      * Set the tooltip for the tab.
41185      * @param {String} tooltip The tab's tooltip
41186      */
41187     setTooltip : function(text){
41188         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41189             this.textEl.dom.qtip = text;
41190             this.textEl.dom.removeAttribute('title');
41191         }else{
41192             this.textEl.dom.title = text;
41193         }
41194     },
41195
41196     onTabClick : function(e){
41197         e.preventDefault();
41198         this.tabPanel.activate(this.id);
41199     },
41200
41201     onTabMouseDown : function(e){
41202         e.preventDefault();
41203         this.tabPanel.activate(this.id);
41204     },
41205 /*
41206     getWidth : function(){
41207         return this.inner.getWidth();
41208     },
41209
41210     setWidth : function(width){
41211         var iwidth = width - this.linode.getPadding("lr");
41212         this.inner.setWidth(iwidth);
41213         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41214         this.linode.setWidth(width);
41215     },
41216 */
41217     /**
41218      * Show or hide the tab
41219      * @param {Boolean} hidden True to hide or false to show.
41220      */
41221     setHidden : function(hidden){
41222         this.hidden = hidden;
41223         this.linode.setStyle("display", hidden ? "none" : "");
41224     },
41225
41226     /**
41227      * Returns true if this tab is "hidden"
41228      * @return {Boolean}
41229      */
41230     isHidden : function(){
41231         return this.hidden;
41232     },
41233
41234     /**
41235      * Returns the text for this tab
41236      * @return {String}
41237      */
41238     getText : function(){
41239         return this.text;
41240     },
41241     /*
41242     autoSize : function(){
41243         //this.el.beginMeasure();
41244         this.textEl.setWidth(1);
41245         /*
41246          *  #2804 [new] Tabs in Roojs
41247          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41248          */
41249         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41250         //this.el.endMeasure();
41251     //},
41252
41253     /**
41254      * Sets the text for the tab (Note: this also sets the tooltip text)
41255      * @param {String} text The tab's text and tooltip
41256      */
41257     setText : function(text){
41258         this.text = text;
41259         this.textEl.update(text);
41260         this.setTooltip(text);
41261         //if(!this.tabPanel.resizeTabs){
41262         //    this.autoSize();
41263         //}
41264     },
41265     /**
41266      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41267      */
41268     activate : function(){
41269         this.tabPanel.activate(this.id);
41270     },
41271
41272     /**
41273      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41274      */
41275     disable : function(){
41276         if(this.tabPanel.active != this){
41277             this.disabled = true;
41278             this.status_node.addClass("disabled");
41279         }
41280     },
41281
41282     /**
41283      * Enables this TabPanelItem if it was previously disabled.
41284      */
41285     enable : function(){
41286         this.disabled = false;
41287         this.status_node.removeClass("disabled");
41288     },
41289
41290     /**
41291      * Sets the content for this TabPanelItem.
41292      * @param {String} content The content
41293      * @param {Boolean} loadScripts true to look for and load scripts
41294      */
41295     setContent : function(content, loadScripts){
41296         this.bodyEl.update(content, loadScripts);
41297     },
41298
41299     /**
41300      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41301      * @return {Roo.UpdateManager} The UpdateManager
41302      */
41303     getUpdateManager : function(){
41304         return this.bodyEl.getUpdateManager();
41305     },
41306
41307     /**
41308      * Set a URL to be used to load the content for this TabPanelItem.
41309      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41310      * @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)
41311      * @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)
41312      * @return {Roo.UpdateManager} The UpdateManager
41313      */
41314     setUrl : function(url, params, loadOnce){
41315         if(this.refreshDelegate){
41316             this.un('activate', this.refreshDelegate);
41317         }
41318         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41319         this.on("activate", this.refreshDelegate);
41320         return this.bodyEl.getUpdateManager();
41321     },
41322
41323     /** @private */
41324     _handleRefresh : function(url, params, loadOnce){
41325         if(!loadOnce || !this.loaded){
41326             var updater = this.bodyEl.getUpdateManager();
41327             updater.update(url, params, this._setLoaded.createDelegate(this));
41328         }
41329     },
41330
41331     /**
41332      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41333      *   Will fail silently if the setUrl method has not been called.
41334      *   This does not activate the panel, just updates its content.
41335      */
41336     refresh : function(){
41337         if(this.refreshDelegate){
41338            this.loaded = false;
41339            this.refreshDelegate();
41340         }
41341     },
41342
41343     /** @private */
41344     _setLoaded : function(){
41345         this.loaded = true;
41346     },
41347
41348     /** @private */
41349     closeClick : function(e){
41350         var o = {};
41351         e.stopEvent();
41352         this.fireEvent("beforeclose", this, o);
41353         if(o.cancel !== true){
41354             this.tabPanel.removeTab(this.id);
41355         }
41356     },
41357     /**
41358      * The text displayed in the tooltip for the close icon.
41359      * @type String
41360      */
41361     closeText : "Close this tab"
41362 });
41363 /**
41364 *    This script refer to:
41365 *    Title: International Telephone Input
41366 *    Author: Jack O'Connor
41367 *    Code version:  v12.1.12
41368 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41369 **/
41370
41371 Roo.bootstrap.PhoneInputData = function() {
41372     var d = [
41373       [
41374         "Afghanistan (‫افغانستان‬‎)",
41375         "af",
41376         "93"
41377       ],
41378       [
41379         "Albania (Shqipëri)",
41380         "al",
41381         "355"
41382       ],
41383       [
41384         "Algeria (‫الجزائر‬‎)",
41385         "dz",
41386         "213"
41387       ],
41388       [
41389         "American Samoa",
41390         "as",
41391         "1684"
41392       ],
41393       [
41394         "Andorra",
41395         "ad",
41396         "376"
41397       ],
41398       [
41399         "Angola",
41400         "ao",
41401         "244"
41402       ],
41403       [
41404         "Anguilla",
41405         "ai",
41406         "1264"
41407       ],
41408       [
41409         "Antigua and Barbuda",
41410         "ag",
41411         "1268"
41412       ],
41413       [
41414         "Argentina",
41415         "ar",
41416         "54"
41417       ],
41418       [
41419         "Armenia (Հայաստան)",
41420         "am",
41421         "374"
41422       ],
41423       [
41424         "Aruba",
41425         "aw",
41426         "297"
41427       ],
41428       [
41429         "Australia",
41430         "au",
41431         "61",
41432         0
41433       ],
41434       [
41435         "Austria (Österreich)",
41436         "at",
41437         "43"
41438       ],
41439       [
41440         "Azerbaijan (Azərbaycan)",
41441         "az",
41442         "994"
41443       ],
41444       [
41445         "Bahamas",
41446         "bs",
41447         "1242"
41448       ],
41449       [
41450         "Bahrain (‫البحرين‬‎)",
41451         "bh",
41452         "973"
41453       ],
41454       [
41455         "Bangladesh (বাংলাদেশ)",
41456         "bd",
41457         "880"
41458       ],
41459       [
41460         "Barbados",
41461         "bb",
41462         "1246"
41463       ],
41464       [
41465         "Belarus (Беларусь)",
41466         "by",
41467         "375"
41468       ],
41469       [
41470         "Belgium (België)",
41471         "be",
41472         "32"
41473       ],
41474       [
41475         "Belize",
41476         "bz",
41477         "501"
41478       ],
41479       [
41480         "Benin (Bénin)",
41481         "bj",
41482         "229"
41483       ],
41484       [
41485         "Bermuda",
41486         "bm",
41487         "1441"
41488       ],
41489       [
41490         "Bhutan (འབྲུག)",
41491         "bt",
41492         "975"
41493       ],
41494       [
41495         "Bolivia",
41496         "bo",
41497         "591"
41498       ],
41499       [
41500         "Bosnia and Herzegovina (Босна и Херцеговина)",
41501         "ba",
41502         "387"
41503       ],
41504       [
41505         "Botswana",
41506         "bw",
41507         "267"
41508       ],
41509       [
41510         "Brazil (Brasil)",
41511         "br",
41512         "55"
41513       ],
41514       [
41515         "British Indian Ocean Territory",
41516         "io",
41517         "246"
41518       ],
41519       [
41520         "British Virgin Islands",
41521         "vg",
41522         "1284"
41523       ],
41524       [
41525         "Brunei",
41526         "bn",
41527         "673"
41528       ],
41529       [
41530         "Bulgaria (България)",
41531         "bg",
41532         "359"
41533       ],
41534       [
41535         "Burkina Faso",
41536         "bf",
41537         "226"
41538       ],
41539       [
41540         "Burundi (Uburundi)",
41541         "bi",
41542         "257"
41543       ],
41544       [
41545         "Cambodia (កម្ពុជា)",
41546         "kh",
41547         "855"
41548       ],
41549       [
41550         "Cameroon (Cameroun)",
41551         "cm",
41552         "237"
41553       ],
41554       [
41555         "Canada",
41556         "ca",
41557         "1",
41558         1,
41559         ["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"]
41560       ],
41561       [
41562         "Cape Verde (Kabu Verdi)",
41563         "cv",
41564         "238"
41565       ],
41566       [
41567         "Caribbean Netherlands",
41568         "bq",
41569         "599",
41570         1
41571       ],
41572       [
41573         "Cayman Islands",
41574         "ky",
41575         "1345"
41576       ],
41577       [
41578         "Central African Republic (République centrafricaine)",
41579         "cf",
41580         "236"
41581       ],
41582       [
41583         "Chad (Tchad)",
41584         "td",
41585         "235"
41586       ],
41587       [
41588         "Chile",
41589         "cl",
41590         "56"
41591       ],
41592       [
41593         "China (中国)",
41594         "cn",
41595         "86"
41596       ],
41597       [
41598         "Christmas Island",
41599         "cx",
41600         "61",
41601         2
41602       ],
41603       [
41604         "Cocos (Keeling) Islands",
41605         "cc",
41606         "61",
41607         1
41608       ],
41609       [
41610         "Colombia",
41611         "co",
41612         "57"
41613       ],
41614       [
41615         "Comoros (‫جزر القمر‬‎)",
41616         "km",
41617         "269"
41618       ],
41619       [
41620         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41621         "cd",
41622         "243"
41623       ],
41624       [
41625         "Congo (Republic) (Congo-Brazzaville)",
41626         "cg",
41627         "242"
41628       ],
41629       [
41630         "Cook Islands",
41631         "ck",
41632         "682"
41633       ],
41634       [
41635         "Costa Rica",
41636         "cr",
41637         "506"
41638       ],
41639       [
41640         "Côte d’Ivoire",
41641         "ci",
41642         "225"
41643       ],
41644       [
41645         "Croatia (Hrvatska)",
41646         "hr",
41647         "385"
41648       ],
41649       [
41650         "Cuba",
41651         "cu",
41652         "53"
41653       ],
41654       [
41655         "Curaçao",
41656         "cw",
41657         "599",
41658         0
41659       ],
41660       [
41661         "Cyprus (Κύπρος)",
41662         "cy",
41663         "357"
41664       ],
41665       [
41666         "Czech Republic (Česká republika)",
41667         "cz",
41668         "420"
41669       ],
41670       [
41671         "Denmark (Danmark)",
41672         "dk",
41673         "45"
41674       ],
41675       [
41676         "Djibouti",
41677         "dj",
41678         "253"
41679       ],
41680       [
41681         "Dominica",
41682         "dm",
41683         "1767"
41684       ],
41685       [
41686         "Dominican Republic (República Dominicana)",
41687         "do",
41688         "1",
41689         2,
41690         ["809", "829", "849"]
41691       ],
41692       [
41693         "Ecuador",
41694         "ec",
41695         "593"
41696       ],
41697       [
41698         "Egypt (‫مصر‬‎)",
41699         "eg",
41700         "20"
41701       ],
41702       [
41703         "El Salvador",
41704         "sv",
41705         "503"
41706       ],
41707       [
41708         "Equatorial Guinea (Guinea Ecuatorial)",
41709         "gq",
41710         "240"
41711       ],
41712       [
41713         "Eritrea",
41714         "er",
41715         "291"
41716       ],
41717       [
41718         "Estonia (Eesti)",
41719         "ee",
41720         "372"
41721       ],
41722       [
41723         "Ethiopia",
41724         "et",
41725         "251"
41726       ],
41727       [
41728         "Falkland Islands (Islas Malvinas)",
41729         "fk",
41730         "500"
41731       ],
41732       [
41733         "Faroe Islands (Føroyar)",
41734         "fo",
41735         "298"
41736       ],
41737       [
41738         "Fiji",
41739         "fj",
41740         "679"
41741       ],
41742       [
41743         "Finland (Suomi)",
41744         "fi",
41745         "358",
41746         0
41747       ],
41748       [
41749         "France",
41750         "fr",
41751         "33"
41752       ],
41753       [
41754         "French Guiana (Guyane française)",
41755         "gf",
41756         "594"
41757       ],
41758       [
41759         "French Polynesia (Polynésie française)",
41760         "pf",
41761         "689"
41762       ],
41763       [
41764         "Gabon",
41765         "ga",
41766         "241"
41767       ],
41768       [
41769         "Gambia",
41770         "gm",
41771         "220"
41772       ],
41773       [
41774         "Georgia (საქართველო)",
41775         "ge",
41776         "995"
41777       ],
41778       [
41779         "Germany (Deutschland)",
41780         "de",
41781         "49"
41782       ],
41783       [
41784         "Ghana (Gaana)",
41785         "gh",
41786         "233"
41787       ],
41788       [
41789         "Gibraltar",
41790         "gi",
41791         "350"
41792       ],
41793       [
41794         "Greece (Ελλάδα)",
41795         "gr",
41796         "30"
41797       ],
41798       [
41799         "Greenland (Kalaallit Nunaat)",
41800         "gl",
41801         "299"
41802       ],
41803       [
41804         "Grenada",
41805         "gd",
41806         "1473"
41807       ],
41808       [
41809         "Guadeloupe",
41810         "gp",
41811         "590",
41812         0
41813       ],
41814       [
41815         "Guam",
41816         "gu",
41817         "1671"
41818       ],
41819       [
41820         "Guatemala",
41821         "gt",
41822         "502"
41823       ],
41824       [
41825         "Guernsey",
41826         "gg",
41827         "44",
41828         1
41829       ],
41830       [
41831         "Guinea (Guinée)",
41832         "gn",
41833         "224"
41834       ],
41835       [
41836         "Guinea-Bissau (Guiné Bissau)",
41837         "gw",
41838         "245"
41839       ],
41840       [
41841         "Guyana",
41842         "gy",
41843         "592"
41844       ],
41845       [
41846         "Haiti",
41847         "ht",
41848         "509"
41849       ],
41850       [
41851         "Honduras",
41852         "hn",
41853         "504"
41854       ],
41855       [
41856         "Hong Kong (香港)",
41857         "hk",
41858         "852"
41859       ],
41860       [
41861         "Hungary (Magyarország)",
41862         "hu",
41863         "36"
41864       ],
41865       [
41866         "Iceland (Ísland)",
41867         "is",
41868         "354"
41869       ],
41870       [
41871         "India (भारत)",
41872         "in",
41873         "91"
41874       ],
41875       [
41876         "Indonesia",
41877         "id",
41878         "62"
41879       ],
41880       [
41881         "Iran (‫ایران‬‎)",
41882         "ir",
41883         "98"
41884       ],
41885       [
41886         "Iraq (‫العراق‬‎)",
41887         "iq",
41888         "964"
41889       ],
41890       [
41891         "Ireland",
41892         "ie",
41893         "353"
41894       ],
41895       [
41896         "Isle of Man",
41897         "im",
41898         "44",
41899         2
41900       ],
41901       [
41902         "Israel (‫ישראל‬‎)",
41903         "il",
41904         "972"
41905       ],
41906       [
41907         "Italy (Italia)",
41908         "it",
41909         "39",
41910         0
41911       ],
41912       [
41913         "Jamaica",
41914         "jm",
41915         "1876"
41916       ],
41917       [
41918         "Japan (日本)",
41919         "jp",
41920         "81"
41921       ],
41922       [
41923         "Jersey",
41924         "je",
41925         "44",
41926         3
41927       ],
41928       [
41929         "Jordan (‫الأردن‬‎)",
41930         "jo",
41931         "962"
41932       ],
41933       [
41934         "Kazakhstan (Казахстан)",
41935         "kz",
41936         "7",
41937         1
41938       ],
41939       [
41940         "Kenya",
41941         "ke",
41942         "254"
41943       ],
41944       [
41945         "Kiribati",
41946         "ki",
41947         "686"
41948       ],
41949       [
41950         "Kosovo",
41951         "xk",
41952         "383"
41953       ],
41954       [
41955         "Kuwait (‫الكويت‬‎)",
41956         "kw",
41957         "965"
41958       ],
41959       [
41960         "Kyrgyzstan (Кыргызстан)",
41961         "kg",
41962         "996"
41963       ],
41964       [
41965         "Laos (ລາວ)",
41966         "la",
41967         "856"
41968       ],
41969       [
41970         "Latvia (Latvija)",
41971         "lv",
41972         "371"
41973       ],
41974       [
41975         "Lebanon (‫لبنان‬‎)",
41976         "lb",
41977         "961"
41978       ],
41979       [
41980         "Lesotho",
41981         "ls",
41982         "266"
41983       ],
41984       [
41985         "Liberia",
41986         "lr",
41987         "231"
41988       ],
41989       [
41990         "Libya (‫ليبيا‬‎)",
41991         "ly",
41992         "218"
41993       ],
41994       [
41995         "Liechtenstein",
41996         "li",
41997         "423"
41998       ],
41999       [
42000         "Lithuania (Lietuva)",
42001         "lt",
42002         "370"
42003       ],
42004       [
42005         "Luxembourg",
42006         "lu",
42007         "352"
42008       ],
42009       [
42010         "Macau (澳門)",
42011         "mo",
42012         "853"
42013       ],
42014       [
42015         "Macedonia (FYROM) (Македонија)",
42016         "mk",
42017         "389"
42018       ],
42019       [
42020         "Madagascar (Madagasikara)",
42021         "mg",
42022         "261"
42023       ],
42024       [
42025         "Malawi",
42026         "mw",
42027         "265"
42028       ],
42029       [
42030         "Malaysia",
42031         "my",
42032         "60"
42033       ],
42034       [
42035         "Maldives",
42036         "mv",
42037         "960"
42038       ],
42039       [
42040         "Mali",
42041         "ml",
42042         "223"
42043       ],
42044       [
42045         "Malta",
42046         "mt",
42047         "356"
42048       ],
42049       [
42050         "Marshall Islands",
42051         "mh",
42052         "692"
42053       ],
42054       [
42055         "Martinique",
42056         "mq",
42057         "596"
42058       ],
42059       [
42060         "Mauritania (‫موريتانيا‬‎)",
42061         "mr",
42062         "222"
42063       ],
42064       [
42065         "Mauritius (Moris)",
42066         "mu",
42067         "230"
42068       ],
42069       [
42070         "Mayotte",
42071         "yt",
42072         "262",
42073         1
42074       ],
42075       [
42076         "Mexico (México)",
42077         "mx",
42078         "52"
42079       ],
42080       [
42081         "Micronesia",
42082         "fm",
42083         "691"
42084       ],
42085       [
42086         "Moldova (Republica Moldova)",
42087         "md",
42088         "373"
42089       ],
42090       [
42091         "Monaco",
42092         "mc",
42093         "377"
42094       ],
42095       [
42096         "Mongolia (Монгол)",
42097         "mn",
42098         "976"
42099       ],
42100       [
42101         "Montenegro (Crna Gora)",
42102         "me",
42103         "382"
42104       ],
42105       [
42106         "Montserrat",
42107         "ms",
42108         "1664"
42109       ],
42110       [
42111         "Morocco (‫المغرب‬‎)",
42112         "ma",
42113         "212",
42114         0
42115       ],
42116       [
42117         "Mozambique (Moçambique)",
42118         "mz",
42119         "258"
42120       ],
42121       [
42122         "Myanmar (Burma) (မြန်မာ)",
42123         "mm",
42124         "95"
42125       ],
42126       [
42127         "Namibia (Namibië)",
42128         "na",
42129         "264"
42130       ],
42131       [
42132         "Nauru",
42133         "nr",
42134         "674"
42135       ],
42136       [
42137         "Nepal (नेपाल)",
42138         "np",
42139         "977"
42140       ],
42141       [
42142         "Netherlands (Nederland)",
42143         "nl",
42144         "31"
42145       ],
42146       [
42147         "New Caledonia (Nouvelle-Calédonie)",
42148         "nc",
42149         "687"
42150       ],
42151       [
42152         "New Zealand",
42153         "nz",
42154         "64"
42155       ],
42156       [
42157         "Nicaragua",
42158         "ni",
42159         "505"
42160       ],
42161       [
42162         "Niger (Nijar)",
42163         "ne",
42164         "227"
42165       ],
42166       [
42167         "Nigeria",
42168         "ng",
42169         "234"
42170       ],
42171       [
42172         "Niue",
42173         "nu",
42174         "683"
42175       ],
42176       [
42177         "Norfolk Island",
42178         "nf",
42179         "672"
42180       ],
42181       [
42182         "North Korea (조선 민주주의 인민 공화국)",
42183         "kp",
42184         "850"
42185       ],
42186       [
42187         "Northern Mariana Islands",
42188         "mp",
42189         "1670"
42190       ],
42191       [
42192         "Norway (Norge)",
42193         "no",
42194         "47",
42195         0
42196       ],
42197       [
42198         "Oman (‫عُمان‬‎)",
42199         "om",
42200         "968"
42201       ],
42202       [
42203         "Pakistan (‫پاکستان‬‎)",
42204         "pk",
42205         "92"
42206       ],
42207       [
42208         "Palau",
42209         "pw",
42210         "680"
42211       ],
42212       [
42213         "Palestine (‫فلسطين‬‎)",
42214         "ps",
42215         "970"
42216       ],
42217       [
42218         "Panama (Panamá)",
42219         "pa",
42220         "507"
42221       ],
42222       [
42223         "Papua New Guinea",
42224         "pg",
42225         "675"
42226       ],
42227       [
42228         "Paraguay",
42229         "py",
42230         "595"
42231       ],
42232       [
42233         "Peru (Perú)",
42234         "pe",
42235         "51"
42236       ],
42237       [
42238         "Philippines",
42239         "ph",
42240         "63"
42241       ],
42242       [
42243         "Poland (Polska)",
42244         "pl",
42245         "48"
42246       ],
42247       [
42248         "Portugal",
42249         "pt",
42250         "351"
42251       ],
42252       [
42253         "Puerto Rico",
42254         "pr",
42255         "1",
42256         3,
42257         ["787", "939"]
42258       ],
42259       [
42260         "Qatar (‫قطر‬‎)",
42261         "qa",
42262         "974"
42263       ],
42264       [
42265         "Réunion (La Réunion)",
42266         "re",
42267         "262",
42268         0
42269       ],
42270       [
42271         "Romania (România)",
42272         "ro",
42273         "40"
42274       ],
42275       [
42276         "Russia (Россия)",
42277         "ru",
42278         "7",
42279         0
42280       ],
42281       [
42282         "Rwanda",
42283         "rw",
42284         "250"
42285       ],
42286       [
42287         "Saint Barthélemy",
42288         "bl",
42289         "590",
42290         1
42291       ],
42292       [
42293         "Saint Helena",
42294         "sh",
42295         "290"
42296       ],
42297       [
42298         "Saint Kitts and Nevis",
42299         "kn",
42300         "1869"
42301       ],
42302       [
42303         "Saint Lucia",
42304         "lc",
42305         "1758"
42306       ],
42307       [
42308         "Saint Martin (Saint-Martin (partie française))",
42309         "mf",
42310         "590",
42311         2
42312       ],
42313       [
42314         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42315         "pm",
42316         "508"
42317       ],
42318       [
42319         "Saint Vincent and the Grenadines",
42320         "vc",
42321         "1784"
42322       ],
42323       [
42324         "Samoa",
42325         "ws",
42326         "685"
42327       ],
42328       [
42329         "San Marino",
42330         "sm",
42331         "378"
42332       ],
42333       [
42334         "São Tomé and Príncipe (São Tomé e Príncipe)",
42335         "st",
42336         "239"
42337       ],
42338       [
42339         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42340         "sa",
42341         "966"
42342       ],
42343       [
42344         "Senegal (Sénégal)",
42345         "sn",
42346         "221"
42347       ],
42348       [
42349         "Serbia (Србија)",
42350         "rs",
42351         "381"
42352       ],
42353       [
42354         "Seychelles",
42355         "sc",
42356         "248"
42357       ],
42358       [
42359         "Sierra Leone",
42360         "sl",
42361         "232"
42362       ],
42363       [
42364         "Singapore",
42365         "sg",
42366         "65"
42367       ],
42368       [
42369         "Sint Maarten",
42370         "sx",
42371         "1721"
42372       ],
42373       [
42374         "Slovakia (Slovensko)",
42375         "sk",
42376         "421"
42377       ],
42378       [
42379         "Slovenia (Slovenija)",
42380         "si",
42381         "386"
42382       ],
42383       [
42384         "Solomon Islands",
42385         "sb",
42386         "677"
42387       ],
42388       [
42389         "Somalia (Soomaaliya)",
42390         "so",
42391         "252"
42392       ],
42393       [
42394         "South Africa",
42395         "za",
42396         "27"
42397       ],
42398       [
42399         "South Korea (대한민국)",
42400         "kr",
42401         "82"
42402       ],
42403       [
42404         "South Sudan (‫جنوب السودان‬‎)",
42405         "ss",
42406         "211"
42407       ],
42408       [
42409         "Spain (España)",
42410         "es",
42411         "34"
42412       ],
42413       [
42414         "Sri Lanka (ශ්‍රී ලංකාව)",
42415         "lk",
42416         "94"
42417       ],
42418       [
42419         "Sudan (‫السودان‬‎)",
42420         "sd",
42421         "249"
42422       ],
42423       [
42424         "Suriname",
42425         "sr",
42426         "597"
42427       ],
42428       [
42429         "Svalbard and Jan Mayen",
42430         "sj",
42431         "47",
42432         1
42433       ],
42434       [
42435         "Swaziland",
42436         "sz",
42437         "268"
42438       ],
42439       [
42440         "Sweden (Sverige)",
42441         "se",
42442         "46"
42443       ],
42444       [
42445         "Switzerland (Schweiz)",
42446         "ch",
42447         "41"
42448       ],
42449       [
42450         "Syria (‫سوريا‬‎)",
42451         "sy",
42452         "963"
42453       ],
42454       [
42455         "Taiwan (台灣)",
42456         "tw",
42457         "886"
42458       ],
42459       [
42460         "Tajikistan",
42461         "tj",
42462         "992"
42463       ],
42464       [
42465         "Tanzania",
42466         "tz",
42467         "255"
42468       ],
42469       [
42470         "Thailand (ไทย)",
42471         "th",
42472         "66"
42473       ],
42474       [
42475         "Timor-Leste",
42476         "tl",
42477         "670"
42478       ],
42479       [
42480         "Togo",
42481         "tg",
42482         "228"
42483       ],
42484       [
42485         "Tokelau",
42486         "tk",
42487         "690"
42488       ],
42489       [
42490         "Tonga",
42491         "to",
42492         "676"
42493       ],
42494       [
42495         "Trinidad and Tobago",
42496         "tt",
42497         "1868"
42498       ],
42499       [
42500         "Tunisia (‫تونس‬‎)",
42501         "tn",
42502         "216"
42503       ],
42504       [
42505         "Turkey (Türkiye)",
42506         "tr",
42507         "90"
42508       ],
42509       [
42510         "Turkmenistan",
42511         "tm",
42512         "993"
42513       ],
42514       [
42515         "Turks and Caicos Islands",
42516         "tc",
42517         "1649"
42518       ],
42519       [
42520         "Tuvalu",
42521         "tv",
42522         "688"
42523       ],
42524       [
42525         "U.S. Virgin Islands",
42526         "vi",
42527         "1340"
42528       ],
42529       [
42530         "Uganda",
42531         "ug",
42532         "256"
42533       ],
42534       [
42535         "Ukraine (Україна)",
42536         "ua",
42537         "380"
42538       ],
42539       [
42540         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42541         "ae",
42542         "971"
42543       ],
42544       [
42545         "United Kingdom",
42546         "gb",
42547         "44",
42548         0
42549       ],
42550       [
42551         "United States",
42552         "us",
42553         "1",
42554         0
42555       ],
42556       [
42557         "Uruguay",
42558         "uy",
42559         "598"
42560       ],
42561       [
42562         "Uzbekistan (Oʻzbekiston)",
42563         "uz",
42564         "998"
42565       ],
42566       [
42567         "Vanuatu",
42568         "vu",
42569         "678"
42570       ],
42571       [
42572         "Vatican City (Città del Vaticano)",
42573         "va",
42574         "39",
42575         1
42576       ],
42577       [
42578         "Venezuela",
42579         "ve",
42580         "58"
42581       ],
42582       [
42583         "Vietnam (Việt Nam)",
42584         "vn",
42585         "84"
42586       ],
42587       [
42588         "Wallis and Futuna (Wallis-et-Futuna)",
42589         "wf",
42590         "681"
42591       ],
42592       [
42593         "Western Sahara (‫الصحراء الغربية‬‎)",
42594         "eh",
42595         "212",
42596         1
42597       ],
42598       [
42599         "Yemen (‫اليمن‬‎)",
42600         "ye",
42601         "967"
42602       ],
42603       [
42604         "Zambia",
42605         "zm",
42606         "260"
42607       ],
42608       [
42609         "Zimbabwe",
42610         "zw",
42611         "263"
42612       ],
42613       [
42614         "Åland Islands",
42615         "ax",
42616         "358",
42617         1
42618       ]
42619   ];
42620   
42621   return d;
42622 }/**
42623 *    This script refer to:
42624 *    Title: International Telephone Input
42625 *    Author: Jack O'Connor
42626 *    Code version:  v12.1.12
42627 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42628 **/
42629
42630 /**
42631  * @class Roo.bootstrap.PhoneInput
42632  * @extends Roo.bootstrap.TriggerField
42633  * An input with International dial-code selection
42634  
42635  * @cfg {String} defaultDialCode default '+852'
42636  * @cfg {Array} preferedCountries default []
42637   
42638  * @constructor
42639  * Create a new PhoneInput.
42640  * @param {Object} config Configuration options
42641  */
42642
42643 Roo.bootstrap.PhoneInput = function(config) {
42644     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42645 };
42646
42647 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42648         
42649         listWidth: undefined,
42650         
42651         selectedClass: 'active',
42652         
42653         invalidClass : "has-warning",
42654         
42655         validClass: 'has-success',
42656         
42657         allowed: '0123456789',
42658         
42659         max_length: 15,
42660         
42661         /**
42662          * @cfg {String} defaultDialCode The default dial code when initializing the input
42663          */
42664         defaultDialCode: '+852',
42665         
42666         /**
42667          * @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
42668          */
42669         preferedCountries: false,
42670         
42671         getAutoCreate : function()
42672         {
42673             var data = Roo.bootstrap.PhoneInputData();
42674             var align = this.labelAlign || this.parentLabelAlign();
42675             var id = Roo.id();
42676             
42677             this.allCountries = [];
42678             this.dialCodeMapping = [];
42679             
42680             for (var i = 0; i < data.length; i++) {
42681               var c = data[i];
42682               this.allCountries[i] = {
42683                 name: c[0],
42684                 iso2: c[1],
42685                 dialCode: c[2],
42686                 priority: c[3] || 0,
42687                 areaCodes: c[4] || null
42688               };
42689               this.dialCodeMapping[c[2]] = {
42690                   name: c[0],
42691                   iso2: c[1],
42692                   priority: c[3] || 0,
42693                   areaCodes: c[4] || null
42694               };
42695             }
42696             
42697             var cfg = {
42698                 cls: 'form-group',
42699                 cn: []
42700             };
42701             
42702             var input =  {
42703                 tag: 'input',
42704                 id : id,
42705                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42706                 maxlength: this.max_length,
42707                 cls : 'form-control tel-input',
42708                 autocomplete: 'new-password'
42709             };
42710             
42711             var hiddenInput = {
42712                 tag: 'input',
42713                 type: 'hidden',
42714                 cls: 'hidden-tel-input'
42715             };
42716             
42717             if (this.name) {
42718                 hiddenInput.name = this.name;
42719             }
42720             
42721             if (this.disabled) {
42722                 input.disabled = true;
42723             }
42724             
42725             var flag_container = {
42726                 tag: 'div',
42727                 cls: 'flag-box',
42728                 cn: [
42729                     {
42730                         tag: 'div',
42731                         cls: 'flag'
42732                     },
42733                     {
42734                         tag: 'div',
42735                         cls: 'caret'
42736                     }
42737                 ]
42738             };
42739             
42740             var box = {
42741                 tag: 'div',
42742                 cls: this.hasFeedback ? 'has-feedback' : '',
42743                 cn: [
42744                     hiddenInput,
42745                     input,
42746                     {
42747                         tag: 'input',
42748                         cls: 'dial-code-holder',
42749                         disabled: true
42750                     }
42751                 ]
42752             };
42753             
42754             var container = {
42755                 cls: 'roo-select2-container input-group',
42756                 cn: [
42757                     flag_container,
42758                     box
42759                 ]
42760             };
42761             
42762             if (this.fieldLabel.length) {
42763                 var indicator = {
42764                     tag: 'i',
42765                     tooltip: 'This field is required'
42766                 };
42767                 
42768                 var label = {
42769                     tag: 'label',
42770                     'for':  id,
42771                     cls: 'control-label',
42772                     cn: []
42773                 };
42774                 
42775                 var label_text = {
42776                     tag: 'span',
42777                     html: this.fieldLabel
42778                 };
42779                 
42780                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42781                 label.cn = [
42782                     indicator,
42783                     label_text
42784                 ];
42785                 
42786                 if(this.indicatorpos == 'right') {
42787                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42788                     label.cn = [
42789                         label_text,
42790                         indicator
42791                     ];
42792                 }
42793                 
42794                 if(align == 'left') {
42795                     container = {
42796                         tag: 'div',
42797                         cn: [
42798                             container
42799                         ]
42800                     };
42801                     
42802                     if(this.labelWidth > 12){
42803                         label.style = "width: " + this.labelWidth + 'px';
42804                     }
42805                     if(this.labelWidth < 13 && this.labelmd == 0){
42806                         this.labelmd = this.labelWidth;
42807                     }
42808                     if(this.labellg > 0){
42809                         label.cls += ' col-lg-' + this.labellg;
42810                         input.cls += ' col-lg-' + (12 - this.labellg);
42811                     }
42812                     if(this.labelmd > 0){
42813                         label.cls += ' col-md-' + this.labelmd;
42814                         container.cls += ' col-md-' + (12 - this.labelmd);
42815                     }
42816                     if(this.labelsm > 0){
42817                         label.cls += ' col-sm-' + this.labelsm;
42818                         container.cls += ' col-sm-' + (12 - this.labelsm);
42819                     }
42820                     if(this.labelxs > 0){
42821                         label.cls += ' col-xs-' + this.labelxs;
42822                         container.cls += ' col-xs-' + (12 - this.labelxs);
42823                     }
42824                 }
42825             }
42826             
42827             cfg.cn = [
42828                 label,
42829                 container
42830             ];
42831             
42832             var settings = this;
42833             
42834             ['xs','sm','md','lg'].map(function(size){
42835                 if (settings[size]) {
42836                     cfg.cls += ' col-' + size + '-' + settings[size];
42837                 }
42838             });
42839             
42840             this.store = new Roo.data.Store({
42841                 proxy : new Roo.data.MemoryProxy({}),
42842                 reader : new Roo.data.JsonReader({
42843                     fields : [
42844                         {
42845                             'name' : 'name',
42846                             'type' : 'string'
42847                         },
42848                         {
42849                             'name' : 'iso2',
42850                             'type' : 'string'
42851                         },
42852                         {
42853                             'name' : 'dialCode',
42854                             'type' : 'string'
42855                         },
42856                         {
42857                             'name' : 'priority',
42858                             'type' : 'string'
42859                         },
42860                         {
42861                             'name' : 'areaCodes',
42862                             'type' : 'string'
42863                         }
42864                     ]
42865                 })
42866             });
42867             
42868             if(!this.preferedCountries) {
42869                 this.preferedCountries = [
42870                     'hk',
42871                     'gb',
42872                     'us'
42873                 ];
42874             }
42875             
42876             var p = this.preferedCountries.reverse();
42877             
42878             if(p) {
42879                 for (var i = 0; i < p.length; i++) {
42880                     for (var j = 0; j < this.allCountries.length; j++) {
42881                         if(this.allCountries[j].iso2 == p[i]) {
42882                             var t = this.allCountries[j];
42883                             this.allCountries.splice(j,1);
42884                             this.allCountries.unshift(t);
42885                         }
42886                     } 
42887                 }
42888             }
42889             
42890             this.store.proxy.data = {
42891                 success: true,
42892                 data: this.allCountries
42893             };
42894             
42895             return cfg;
42896         },
42897         
42898         initEvents : function()
42899         {
42900             this.createList();
42901             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42902             
42903             this.indicator = this.indicatorEl();
42904             this.flag = this.flagEl();
42905             this.dialCodeHolder = this.dialCodeHolderEl();
42906             
42907             this.trigger = this.el.select('div.flag-box',true).first();
42908             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42909             
42910             var _this = this;
42911             
42912             (function(){
42913                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42914                 _this.list.setWidth(lw);
42915             }).defer(100);
42916             
42917             this.list.on('mouseover', this.onViewOver, this);
42918             this.list.on('mousemove', this.onViewMove, this);
42919             this.inputEl().on("keyup", this.onKeyUp, this);
42920             this.inputEl().on("keypress", this.onKeyPress, this);
42921             
42922             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42923
42924             this.view = new Roo.View(this.list, this.tpl, {
42925                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42926             });
42927             
42928             this.view.on('click', this.onViewClick, this);
42929             this.setValue(this.defaultDialCode);
42930         },
42931         
42932         onTriggerClick : function(e)
42933         {
42934             Roo.log('trigger click');
42935             if(this.disabled){
42936                 return;
42937             }
42938             
42939             if(this.isExpanded()){
42940                 this.collapse();
42941                 this.hasFocus = false;
42942             }else {
42943                 this.store.load({});
42944                 this.hasFocus = true;
42945                 this.expand();
42946             }
42947         },
42948         
42949         isExpanded : function()
42950         {
42951             return this.list.isVisible();
42952         },
42953         
42954         collapse : function()
42955         {
42956             if(!this.isExpanded()){
42957                 return;
42958             }
42959             this.list.hide();
42960             Roo.get(document).un('mousedown', this.collapseIf, this);
42961             Roo.get(document).un('mousewheel', this.collapseIf, this);
42962             this.fireEvent('collapse', this);
42963             this.validate();
42964         },
42965         
42966         expand : function()
42967         {
42968             Roo.log('expand');
42969
42970             if(this.isExpanded() || !this.hasFocus){
42971                 return;
42972             }
42973             
42974             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42975             this.list.setWidth(lw);
42976             
42977             this.list.show();
42978             this.restrictHeight();
42979             
42980             Roo.get(document).on('mousedown', this.collapseIf, this);
42981             Roo.get(document).on('mousewheel', this.collapseIf, this);
42982             
42983             this.fireEvent('expand', this);
42984         },
42985         
42986         restrictHeight : function()
42987         {
42988             this.list.alignTo(this.inputEl(), this.listAlign);
42989             this.list.alignTo(this.inputEl(), this.listAlign);
42990         },
42991         
42992         onViewOver : function(e, t)
42993         {
42994             if(this.inKeyMode){
42995                 return;
42996             }
42997             var item = this.view.findItemFromChild(t);
42998             
42999             if(item){
43000                 var index = this.view.indexOf(item);
43001                 this.select(index, false);
43002             }
43003         },
43004
43005         // private
43006         onViewClick : function(view, doFocus, el, e)
43007         {
43008             var index = this.view.getSelectedIndexes()[0];
43009             
43010             var r = this.store.getAt(index);
43011             
43012             if(r){
43013                 this.onSelect(r, index);
43014             }
43015             if(doFocus !== false && !this.blockFocus){
43016                 this.inputEl().focus();
43017             }
43018         },
43019         
43020         onViewMove : function(e, t)
43021         {
43022             this.inKeyMode = false;
43023         },
43024         
43025         select : function(index, scrollIntoView)
43026         {
43027             this.selectedIndex = index;
43028             this.view.select(index);
43029             if(scrollIntoView !== false){
43030                 var el = this.view.getNode(index);
43031                 if(el){
43032                     this.list.scrollChildIntoView(el, false);
43033                 }
43034             }
43035         },
43036         
43037         createList : function()
43038         {
43039             this.list = Roo.get(document.body).createChild({
43040                 tag: 'ul',
43041                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43042                 style: 'display:none'
43043             });
43044             
43045             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43046         },
43047         
43048         collapseIf : function(e)
43049         {
43050             var in_combo  = e.within(this.el);
43051             var in_list =  e.within(this.list);
43052             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43053             
43054             if (in_combo || in_list || is_list) {
43055                 return;
43056             }
43057             this.collapse();
43058         },
43059         
43060         onSelect : function(record, index)
43061         {
43062             if(this.fireEvent('beforeselect', this, record, index) !== false){
43063                 
43064                 this.setFlagClass(record.data.iso2);
43065                 this.setDialCode(record.data.dialCode);
43066                 this.hasFocus = false;
43067                 this.collapse();
43068                 this.fireEvent('select', this, record, index);
43069             }
43070         },
43071         
43072         flagEl : function()
43073         {
43074             var flag = this.el.select('div.flag',true).first();
43075             if(!flag){
43076                 return false;
43077             }
43078             return flag;
43079         },
43080         
43081         dialCodeHolderEl : function()
43082         {
43083             var d = this.el.select('input.dial-code-holder',true).first();
43084             if(!d){
43085                 return false;
43086             }
43087             return d;
43088         },
43089         
43090         setDialCode : function(v)
43091         {
43092             this.dialCodeHolder.dom.value = '+'+v;
43093         },
43094         
43095         setFlagClass : function(n)
43096         {
43097             this.flag.dom.className = 'flag '+n;
43098         },
43099         
43100         getValue : function()
43101         {
43102             var v = this.inputEl().getValue();
43103             if(this.dialCodeHolder) {
43104                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43105             }
43106             return v;
43107         },
43108         
43109         setValue : function(v)
43110         {
43111             var d = this.getDialCode(v);
43112             
43113             //invalid dial code
43114             if(v.length == 0 || !d || d.length == 0) {
43115                 if(this.rendered){
43116                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43117                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43118                 }
43119                 return;
43120             }
43121             
43122             //valid dial code
43123             this.setFlagClass(this.dialCodeMapping[d].iso2);
43124             this.setDialCode(d);
43125             this.inputEl().dom.value = v.replace('+'+d,'');
43126             this.hiddenEl().dom.value = this.getValue();
43127             
43128             this.validate();
43129         },
43130         
43131         getDialCode : function(v)
43132         {
43133             v = v ||  '';
43134             
43135             if (v.length == 0) {
43136                 return this.dialCodeHolder.dom.value;
43137             }
43138             
43139             var dialCode = "";
43140             if (v.charAt(0) != "+") {
43141                 return false;
43142             }
43143             var numericChars = "";
43144             for (var i = 1; i < v.length; i++) {
43145               var c = v.charAt(i);
43146               if (!isNaN(c)) {
43147                 numericChars += c;
43148                 if (this.dialCodeMapping[numericChars]) {
43149                   dialCode = v.substr(1, i);
43150                 }
43151                 if (numericChars.length == 4) {
43152                   break;
43153                 }
43154               }
43155             }
43156             return dialCode;
43157         },
43158         
43159         reset : function()
43160         {
43161             this.setValue(this.defaultDialCode);
43162             this.validate();
43163         },
43164         
43165         hiddenEl : function()
43166         {
43167             return this.el.select('input.hidden-tel-input',true).first();
43168         },
43169         
43170         // after setting val
43171         onKeyUp : function(e){
43172             this.setValue(this.getValue());
43173         },
43174         
43175         onKeyPress : function(e){
43176             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43177                 e.stopEvent();
43178             }
43179         }
43180         
43181 });
43182 /**
43183  * @class Roo.bootstrap.MoneyField
43184  * @extends Roo.bootstrap.ComboBox
43185  * Bootstrap MoneyField class
43186  * 
43187  * @constructor
43188  * Create a new MoneyField.
43189  * @param {Object} config Configuration options
43190  */
43191
43192 Roo.bootstrap.MoneyField = function(config) {
43193     
43194     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43195     
43196 };
43197
43198 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43199     
43200     /**
43201      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43202      */
43203     allowDecimals : true,
43204     /**
43205      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43206      */
43207     decimalSeparator : ".",
43208     /**
43209      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43210      */
43211     decimalPrecision : 0,
43212     /**
43213      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43214      */
43215     allowNegative : true,
43216     /**
43217      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43218      */
43219     allowZero: true,
43220     /**
43221      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43222      */
43223     minValue : Number.NEGATIVE_INFINITY,
43224     /**
43225      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43226      */
43227     maxValue : Number.MAX_VALUE,
43228     /**
43229      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43230      */
43231     minText : "The minimum value for this field is {0}",
43232     /**
43233      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43234      */
43235     maxText : "The maximum value for this field is {0}",
43236     /**
43237      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43238      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43239      */
43240     nanText : "{0} is not a valid number",
43241     /**
43242      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43243      */
43244     castInt : true,
43245     /**
43246      * @cfg {String} defaults currency of the MoneyField
43247      * value should be in lkey
43248      */
43249     defaultCurrency : false,
43250     /**
43251      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43252      */
43253     thousandsDelimiter : false,
43254     /**
43255      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43256      */
43257     max_length: false,
43258     
43259     inputlg : 9,
43260     inputmd : 9,
43261     inputsm : 9,
43262     inputxs : 6,
43263     
43264     store : false,
43265     
43266     getAutoCreate : function()
43267     {
43268         var align = this.labelAlign || this.parentLabelAlign();
43269         
43270         var id = Roo.id();
43271
43272         var cfg = {
43273             cls: 'form-group',
43274             cn: []
43275         };
43276
43277         var input =  {
43278             tag: 'input',
43279             id : id,
43280             cls : 'form-control roo-money-amount-input',
43281             autocomplete: 'new-password'
43282         };
43283         
43284         var hiddenInput = {
43285             tag: 'input',
43286             type: 'hidden',
43287             id: Roo.id(),
43288             cls: 'hidden-number-input'
43289         };
43290         
43291         if(this.max_length) {
43292             input.maxlength = this.max_length; 
43293         }
43294         
43295         if (this.name) {
43296             hiddenInput.name = this.name;
43297         }
43298
43299         if (this.disabled) {
43300             input.disabled = true;
43301         }
43302
43303         var clg = 12 - this.inputlg;
43304         var cmd = 12 - this.inputmd;
43305         var csm = 12 - this.inputsm;
43306         var cxs = 12 - this.inputxs;
43307         
43308         var container = {
43309             tag : 'div',
43310             cls : 'row roo-money-field',
43311             cn : [
43312                 {
43313                     tag : 'div',
43314                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43315                     cn : [
43316                         {
43317                             tag : 'div',
43318                             cls: 'roo-select2-container input-group',
43319                             cn: [
43320                                 {
43321                                     tag : 'input',
43322                                     cls : 'form-control roo-money-currency-input',
43323                                     autocomplete: 'new-password',
43324                                     readOnly : 1,
43325                                     name : this.currencyName
43326                                 },
43327                                 {
43328                                     tag :'span',
43329                                     cls : 'input-group-addon',
43330                                     cn : [
43331                                         {
43332                                             tag: 'span',
43333                                             cls: 'caret'
43334                                         }
43335                                     ]
43336                                 }
43337                             ]
43338                         }
43339                     ]
43340                 },
43341                 {
43342                     tag : 'div',
43343                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43344                     cn : [
43345                         {
43346                             tag: 'div',
43347                             cls: this.hasFeedback ? 'has-feedback' : '',
43348                             cn: [
43349                                 input
43350                             ]
43351                         }
43352                     ]
43353                 }
43354             ]
43355             
43356         };
43357         
43358         if (this.fieldLabel.length) {
43359             var indicator = {
43360                 tag: 'i',
43361                 tooltip: 'This field is required'
43362             };
43363
43364             var label = {
43365                 tag: 'label',
43366                 'for':  id,
43367                 cls: 'control-label',
43368                 cn: []
43369             };
43370
43371             var label_text = {
43372                 tag: 'span',
43373                 html: this.fieldLabel
43374             };
43375
43376             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43377             label.cn = [
43378                 indicator,
43379                 label_text
43380             ];
43381
43382             if(this.indicatorpos == 'right') {
43383                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43384                 label.cn = [
43385                     label_text,
43386                     indicator
43387                 ];
43388             }
43389
43390             if(align == 'left') {
43391                 container = {
43392                     tag: 'div',
43393                     cn: [
43394                         container
43395                     ]
43396                 };
43397
43398                 if(this.labelWidth > 12){
43399                     label.style = "width: " + this.labelWidth + 'px';
43400                 }
43401                 if(this.labelWidth < 13 && this.labelmd == 0){
43402                     this.labelmd = this.labelWidth;
43403                 }
43404                 if(this.labellg > 0){
43405                     label.cls += ' col-lg-' + this.labellg;
43406                     input.cls += ' col-lg-' + (12 - this.labellg);
43407                 }
43408                 if(this.labelmd > 0){
43409                     label.cls += ' col-md-' + this.labelmd;
43410                     container.cls += ' col-md-' + (12 - this.labelmd);
43411                 }
43412                 if(this.labelsm > 0){
43413                     label.cls += ' col-sm-' + this.labelsm;
43414                     container.cls += ' col-sm-' + (12 - this.labelsm);
43415                 }
43416                 if(this.labelxs > 0){
43417                     label.cls += ' col-xs-' + this.labelxs;
43418                     container.cls += ' col-xs-' + (12 - this.labelxs);
43419                 }
43420             }
43421         }
43422
43423         cfg.cn = [
43424             label,
43425             container,
43426             hiddenInput
43427         ];
43428         
43429         var settings = this;
43430
43431         ['xs','sm','md','lg'].map(function(size){
43432             if (settings[size]) {
43433                 cfg.cls += ' col-' + size + '-' + settings[size];
43434             }
43435         });
43436         
43437         return cfg;
43438     },
43439     
43440     initEvents : function()
43441     {
43442         this.indicator = this.indicatorEl();
43443         
43444         this.initCurrencyEvent();
43445         
43446         this.initNumberEvent();
43447     },
43448     
43449     initCurrencyEvent : function()
43450     {
43451         if (!this.store) {
43452             throw "can not find store for combo";
43453         }
43454         
43455         this.store = Roo.factory(this.store, Roo.data);
43456         this.store.parent = this;
43457         
43458         this.createList();
43459         
43460         this.triggerEl = this.el.select('.input-group-addon', true).first();
43461         
43462         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43463         
43464         var _this = this;
43465         
43466         (function(){
43467             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43468             _this.list.setWidth(lw);
43469         }).defer(100);
43470         
43471         this.list.on('mouseover', this.onViewOver, this);
43472         this.list.on('mousemove', this.onViewMove, this);
43473         this.list.on('scroll', this.onViewScroll, this);
43474         
43475         if(!this.tpl){
43476             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43477         }
43478         
43479         this.view = new Roo.View(this.list, this.tpl, {
43480             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43481         });
43482         
43483         this.view.on('click', this.onViewClick, this);
43484         
43485         this.store.on('beforeload', this.onBeforeLoad, this);
43486         this.store.on('load', this.onLoad, this);
43487         this.store.on('loadexception', this.onLoadException, this);
43488         
43489         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43490             "up" : function(e){
43491                 this.inKeyMode = true;
43492                 this.selectPrev();
43493             },
43494
43495             "down" : function(e){
43496                 if(!this.isExpanded()){
43497                     this.onTriggerClick();
43498                 }else{
43499                     this.inKeyMode = true;
43500                     this.selectNext();
43501                 }
43502             },
43503
43504             "enter" : function(e){
43505                 this.collapse();
43506                 
43507                 if(this.fireEvent("specialkey", this, e)){
43508                     this.onViewClick(false);
43509                 }
43510                 
43511                 return true;
43512             },
43513
43514             "esc" : function(e){
43515                 this.collapse();
43516             },
43517
43518             "tab" : function(e){
43519                 this.collapse();
43520                 
43521                 if(this.fireEvent("specialkey", this, e)){
43522                     this.onViewClick(false);
43523                 }
43524                 
43525                 return true;
43526             },
43527
43528             scope : this,
43529
43530             doRelay : function(foo, bar, hname){
43531                 if(hname == 'down' || this.scope.isExpanded()){
43532                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43533                 }
43534                 return true;
43535             },
43536
43537             forceKeyDown: true
43538         });
43539         
43540         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43541         
43542     },
43543     
43544     initNumberEvent : function(e)
43545     {
43546         this.inputEl().on("keydown" , this.fireKey,  this);
43547         this.inputEl().on("focus", this.onFocus,  this);
43548         this.inputEl().on("blur", this.onBlur,  this);
43549         
43550         this.inputEl().relayEvent('keyup', this);
43551         
43552         if(this.indicator){
43553             this.indicator.addClass('invisible');
43554         }
43555  
43556         this.originalValue = this.getValue();
43557         
43558         if(this.validationEvent == 'keyup'){
43559             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43560             this.inputEl().on('keyup', this.filterValidation, this);
43561         }
43562         else if(this.validationEvent !== false){
43563             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43564         }
43565         
43566         if(this.selectOnFocus){
43567             this.on("focus", this.preFocus, this);
43568             
43569         }
43570         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43571             this.inputEl().on("keypress", this.filterKeys, this);
43572         } else {
43573             this.inputEl().relayEvent('keypress', this);
43574         }
43575         
43576         var allowed = "0123456789";
43577         
43578         if(this.allowDecimals){
43579             allowed += this.decimalSeparator;
43580         }
43581         
43582         if(this.allowNegative){
43583             allowed += "-";
43584         }
43585         
43586         if(this.thousandsDelimiter) {
43587             allowed += ",";
43588         }
43589         
43590         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43591         
43592         var keyPress = function(e){
43593             
43594             var k = e.getKey();
43595             
43596             var c = e.getCharCode();
43597             
43598             if(
43599                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43600                     allowed.indexOf(String.fromCharCode(c)) === -1
43601             ){
43602                 e.stopEvent();
43603                 return;
43604             }
43605             
43606             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43607                 return;
43608             }
43609             
43610             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43611                 e.stopEvent();
43612             }
43613         };
43614         
43615         this.inputEl().on("keypress", keyPress, this);
43616         
43617     },
43618     
43619     onTriggerClick : function(e)
43620     {   
43621         if(this.disabled){
43622             return;
43623         }
43624         
43625         this.page = 0;
43626         this.loadNext = false;
43627         
43628         if(this.isExpanded()){
43629             this.collapse();
43630             return;
43631         }
43632         
43633         this.hasFocus = true;
43634         
43635         if(this.triggerAction == 'all') {
43636             this.doQuery(this.allQuery, true);
43637             return;
43638         }
43639         
43640         this.doQuery(this.getRawValue());
43641     },
43642     
43643     getCurrency : function()
43644     {   
43645         var v = this.currencyEl().getValue();
43646         
43647         return v;
43648     },
43649     
43650     restrictHeight : function()
43651     {
43652         this.list.alignTo(this.currencyEl(), this.listAlign);
43653         this.list.alignTo(this.currencyEl(), this.listAlign);
43654     },
43655     
43656     onViewClick : function(view, doFocus, el, e)
43657     {
43658         var index = this.view.getSelectedIndexes()[0];
43659         
43660         var r = this.store.getAt(index);
43661         
43662         if(r){
43663             this.onSelect(r, index);
43664         }
43665     },
43666     
43667     onSelect : function(record, index){
43668         
43669         if(this.fireEvent('beforeselect', this, record, index) !== false){
43670         
43671             this.setFromCurrencyData(index > -1 ? record.data : false);
43672             
43673             this.collapse();
43674             
43675             this.fireEvent('select', this, record, index);
43676         }
43677     },
43678     
43679     setFromCurrencyData : function(o)
43680     {
43681         var currency = '';
43682         
43683         this.lastCurrency = o;
43684         
43685         if (this.currencyField) {
43686             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43687         } else {
43688             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43689         }
43690         
43691         this.lastSelectionText = currency;
43692         
43693         //setting default currency
43694         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43695             this.setCurrency(this.defaultCurrency);
43696             return;
43697         }
43698         
43699         this.setCurrency(currency);
43700     },
43701     
43702     setFromData : function(o)
43703     {
43704         var c = {};
43705         
43706         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43707         
43708         this.setFromCurrencyData(c);
43709         
43710         var value = '';
43711         
43712         if (this.name) {
43713             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43714         } else {
43715             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43716         }
43717         
43718         this.setValue(value);
43719         
43720     },
43721     
43722     setCurrency : function(v)
43723     {   
43724         this.currencyValue = v;
43725         
43726         if(this.rendered){
43727             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43728             this.validate();
43729         }
43730     },
43731     
43732     setValue : function(v)
43733     {
43734         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43735         
43736         this.value = v;
43737         
43738         if(this.rendered){
43739             
43740             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43741             
43742             this.inputEl().dom.value = (v == '') ? '' :
43743                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43744             
43745             if(!this.allowZero && v === '0') {
43746                 this.hiddenEl().dom.value = '';
43747                 this.inputEl().dom.value = '';
43748             }
43749             
43750             this.validate();
43751         }
43752     },
43753     
43754     getRawValue : function()
43755     {
43756         var v = this.inputEl().getValue();
43757         
43758         return v;
43759     },
43760     
43761     getValue : function()
43762     {
43763         return this.fixPrecision(this.parseValue(this.getRawValue()));
43764     },
43765     
43766     parseValue : function(value)
43767     {
43768         if(this.thousandsDelimiter) {
43769             value += "";
43770             r = new RegExp(",", "g");
43771             value = value.replace(r, "");
43772         }
43773         
43774         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43775         return isNaN(value) ? '' : value;
43776         
43777     },
43778     
43779     fixPrecision : function(value)
43780     {
43781         if(this.thousandsDelimiter) {
43782             value += "";
43783             r = new RegExp(",", "g");
43784             value = value.replace(r, "");
43785         }
43786         
43787         var nan = isNaN(value);
43788         
43789         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43790             return nan ? '' : value;
43791         }
43792         return parseFloat(value).toFixed(this.decimalPrecision);
43793     },
43794     
43795     decimalPrecisionFcn : function(v)
43796     {
43797         return Math.floor(v);
43798     },
43799     
43800     validateValue : function(value)
43801     {
43802         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43803             return false;
43804         }
43805         
43806         var num = this.parseValue(value);
43807         
43808         if(isNaN(num)){
43809             this.markInvalid(String.format(this.nanText, value));
43810             return false;
43811         }
43812         
43813         if(num < this.minValue){
43814             this.markInvalid(String.format(this.minText, this.minValue));
43815             return false;
43816         }
43817         
43818         if(num > this.maxValue){
43819             this.markInvalid(String.format(this.maxText, this.maxValue));
43820             return false;
43821         }
43822         
43823         return true;
43824     },
43825     
43826     validate : function()
43827     {
43828         if(this.disabled || this.allowBlank){
43829             this.markValid();
43830             return true;
43831         }
43832         
43833         var currency = this.getCurrency();
43834         
43835         if(this.validateValue(this.getRawValue()) && currency.length){
43836             this.markValid();
43837             return true;
43838         }
43839         
43840         this.markInvalid();
43841         return false;
43842     },
43843     
43844     getName: function()
43845     {
43846         return this.name;
43847     },
43848     
43849     beforeBlur : function()
43850     {
43851         if(!this.castInt){
43852             return;
43853         }
43854         
43855         var v = this.parseValue(this.getRawValue());
43856         
43857         if(v || v == 0){
43858             this.setValue(v);
43859         }
43860     },
43861     
43862     onBlur : function()
43863     {
43864         this.beforeBlur();
43865         
43866         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43867             //this.el.removeClass(this.focusClass);
43868         }
43869         
43870         this.hasFocus = false;
43871         
43872         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43873             this.validate();
43874         }
43875         
43876         var v = this.getValue();
43877         
43878         if(String(v) !== String(this.startValue)){
43879             this.fireEvent('change', this, v, this.startValue);
43880         }
43881         
43882         this.fireEvent("blur", this);
43883     },
43884     
43885     inputEl : function()
43886     {
43887         return this.el.select('.roo-money-amount-input', true).first();
43888     },
43889     
43890     currencyEl : function()
43891     {
43892         return this.el.select('.roo-money-currency-input', true).first();
43893     },
43894     
43895     hiddenEl : function()
43896     {
43897         return this.el.select('input.hidden-number-input',true).first();
43898     }
43899     
43900 });/**
43901  * @class Roo.bootstrap.BezierSignature
43902  * @extends Roo.bootstrap.Component
43903  * Bootstrap BezierSignature class
43904  * This script refer to:
43905  *    Title: Signature Pad
43906  *    Author: szimek
43907  *    Availability: https://github.com/szimek/signature_pad
43908  *
43909  * @constructor
43910  * Create a new BezierSignature
43911  * @param {Object} config The config object
43912  */
43913
43914 Roo.bootstrap.BezierSignature = function(config){
43915     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43916     this.addEvents({
43917         "resize" : true
43918     });
43919 };
43920
43921 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43922 {
43923      
43924     curve_data: [],
43925     
43926     is_empty: true,
43927     
43928     mouse_btn_down: true,
43929     
43930     /**
43931      * @cfg {int} canvas height
43932      */
43933     canvas_height: '200px',
43934     
43935     /**
43936      * @cfg {float|function} Radius of a single dot.
43937      */ 
43938     dot_size: false,
43939     
43940     /**
43941      * @cfg {float} Minimum width of a line. Defaults to 0.5.
43942      */
43943     min_width: 0.5,
43944     
43945     /**
43946      * @cfg {float} Maximum width of a line. Defaults to 2.5.
43947      */
43948     max_width: 2.5,
43949     
43950     /**
43951      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43952      */
43953     throttle: 16,
43954     
43955     /**
43956      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43957      */
43958     min_distance: 5,
43959     
43960     /**
43961      * @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.
43962      */
43963     bg_color: 'rgba(0, 0, 0, 0)',
43964     
43965     /**
43966      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43967      */
43968     dot_color: 'black',
43969     
43970     /**
43971      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43972      */ 
43973     velocity_filter_weight: 0.7,
43974     
43975     /**
43976      * @cfg {function} Callback when stroke begin. 
43977      */
43978     onBegin: false,
43979     
43980     /**
43981      * @cfg {function} Callback when stroke end.
43982      */
43983     onEnd: false,
43984     
43985     getAutoCreate : function()
43986     {
43987         var cls = 'roo-signature column';
43988         
43989         if(this.cls){
43990             cls += ' ' + this.cls;
43991         }
43992         
43993         var col_sizes = [
43994             'lg',
43995             'md',
43996             'sm',
43997             'xs'
43998         ];
43999         
44000         for(var i = 0; i < col_sizes.length; i++) {
44001             if(this[col_sizes[i]]) {
44002                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44003             }
44004         }
44005         
44006         var cfg = {
44007             tag: 'div',
44008             cls: cls,
44009             cn: [
44010                 {
44011                     tag: 'div',
44012                     cls: 'roo-signature-body',
44013                     cn: [
44014                         {
44015                             tag: 'canvas',
44016                             cls: 'roo-signature-body-canvas',
44017                             height: this.canvas_height,
44018                             width: this.canvas_width
44019                         }
44020                     ]
44021                 },
44022                 {
44023                     tag: 'input',
44024                     type: 'file',
44025                     style: 'display: none'
44026                 }
44027             ]
44028         };
44029         
44030         return cfg;
44031     },
44032     
44033     initEvents: function() 
44034     {
44035         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44036         
44037         var canvas = this.canvasEl();
44038         
44039         // mouse && touch event swapping...
44040         canvas.dom.style.touchAction = 'none';
44041         canvas.dom.style.msTouchAction = 'none';
44042         
44043         this.mouse_btn_down = false;
44044         canvas.on('mousedown', this._handleMouseDown, this);
44045         canvas.on('mousemove', this._handleMouseMove, this);
44046         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44047         
44048         if (window.PointerEvent) {
44049             canvas.on('pointerdown', this._handleMouseDown, this);
44050             canvas.on('pointermove', this._handleMouseMove, this);
44051             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44052         }
44053         
44054         if ('ontouchstart' in window) {
44055             canvas.on('touchstart', this._handleTouchStart, this);
44056             canvas.on('touchmove', this._handleTouchMove, this);
44057             canvas.on('touchend', this._handleTouchEnd, this);
44058         }
44059         
44060         Roo.EventManager.onWindowResize(this.resize, this, true);
44061         
44062         // file input event
44063         this.fileEl().on('change', this.uploadImage, this);
44064         
44065         this.clear();
44066         
44067         this.resize();
44068     },
44069     
44070     resize: function(){
44071         
44072         var canvas = this.canvasEl().dom;
44073         var ctx = this.canvasElCtx();
44074         var img_data = false;
44075         
44076         if(canvas.width > 0) {
44077             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44078         }
44079         // setting canvas width will clean img data
44080         canvas.width = 0;
44081         
44082         var style = window.getComputedStyle ? 
44083             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44084             
44085         var padding_left = parseInt(style.paddingLeft) || 0;
44086         var padding_right = parseInt(style.paddingRight) || 0;
44087         
44088         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44089         
44090         if(img_data) {
44091             ctx.putImageData(img_data, 0, 0);
44092         }
44093     },
44094     
44095     _handleMouseDown: function(e)
44096     {
44097         if (e.browserEvent.which === 1) {
44098             this.mouse_btn_down = true;
44099             this.strokeBegin(e);
44100         }
44101     },
44102     
44103     _handleMouseMove: function (e)
44104     {
44105         if (this.mouse_btn_down) {
44106             this.strokeMoveUpdate(e);
44107         }
44108     },
44109     
44110     _handleMouseUp: function (e)
44111     {
44112         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44113             this.mouse_btn_down = false;
44114             this.strokeEnd(e);
44115         }
44116     },
44117     
44118     _handleTouchStart: function (e) {
44119         
44120         e.preventDefault();
44121         if (e.browserEvent.targetTouches.length === 1) {
44122             // var touch = e.browserEvent.changedTouches[0];
44123             // this.strokeBegin(touch);
44124             
44125              this.strokeBegin(e); // assume e catching the correct xy...
44126         }
44127     },
44128     
44129     _handleTouchMove: function (e) {
44130         e.preventDefault();
44131         // var touch = event.targetTouches[0];
44132         // _this._strokeMoveUpdate(touch);
44133         this.strokeMoveUpdate(e);
44134     },
44135     
44136     _handleTouchEnd: function (e) {
44137         var wasCanvasTouched = e.target === this.canvasEl().dom;
44138         if (wasCanvasTouched) {
44139             e.preventDefault();
44140             // var touch = event.changedTouches[0];
44141             // _this._strokeEnd(touch);
44142             this.strokeEnd(e);
44143         }
44144     },
44145     
44146     reset: function () {
44147         this._lastPoints = [];
44148         this._lastVelocity = 0;
44149         this._lastWidth = (this.min_width + this.max_width) / 2;
44150         this.canvasElCtx().fillStyle = this.dot_color;
44151     },
44152     
44153     strokeMoveUpdate: function(e)
44154     {
44155         this.strokeUpdate(e);
44156         
44157         if (this.throttle) {
44158             this.throttleStroke(this.strokeUpdate, this.throttle);
44159         }
44160         else {
44161             this.strokeUpdate(e);
44162         }
44163     },
44164     
44165     strokeBegin: function(e)
44166     {
44167         var newPointGroup = {
44168             color: this.dot_color,
44169             points: []
44170         };
44171         
44172         if (typeof this.onBegin === 'function') {
44173             this.onBegin(e);
44174         }
44175         
44176         this.curve_data.push(newPointGroup);
44177         this.reset();
44178         this.strokeUpdate(e);
44179     },
44180     
44181     strokeUpdate: function(e)
44182     {
44183         var rect = this.canvasEl().dom.getBoundingClientRect();
44184         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44185         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44186         var lastPoints = lastPointGroup.points;
44187         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44188         var isLastPointTooClose = lastPoint
44189             ? point.distanceTo(lastPoint) <= this.min_distance
44190             : false;
44191         var color = lastPointGroup.color;
44192         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44193             var curve = this.addPoint(point);
44194             if (!lastPoint) {
44195                 this.drawDot({color: color, point: point});
44196             }
44197             else if (curve) {
44198                 this.drawCurve({color: color, curve: curve});
44199             }
44200             lastPoints.push({
44201                 time: point.time,
44202                 x: point.x,
44203                 y: point.y
44204             });
44205         }
44206     },
44207     
44208     strokeEnd: function(e)
44209     {
44210         this.strokeUpdate(e);
44211         if (typeof this.onEnd === 'function') {
44212             this.onEnd(e);
44213         }
44214     },
44215     
44216     addPoint:  function (point) {
44217         var _lastPoints = this._lastPoints;
44218         _lastPoints.push(point);
44219         if (_lastPoints.length > 2) {
44220             if (_lastPoints.length === 3) {
44221                 _lastPoints.unshift(_lastPoints[0]);
44222             }
44223             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44224             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44225             _lastPoints.shift();
44226             return curve;
44227         }
44228         return null;
44229     },
44230     
44231     calculateCurveWidths: function (startPoint, endPoint) {
44232         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44233             (1 - this.velocity_filter_weight) * this._lastVelocity;
44234
44235         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44236         var widths = {
44237             end: newWidth,
44238             start: this._lastWidth
44239         };
44240         
44241         this._lastVelocity = velocity;
44242         this._lastWidth = newWidth;
44243         return widths;
44244     },
44245     
44246     drawDot: function (_a) {
44247         var color = _a.color, point = _a.point;
44248         var ctx = this.canvasElCtx();
44249         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44250         ctx.beginPath();
44251         this.drawCurveSegment(point.x, point.y, width);
44252         ctx.closePath();
44253         ctx.fillStyle = color;
44254         ctx.fill();
44255     },
44256     
44257     drawCurve: function (_a) {
44258         var color = _a.color, curve = _a.curve;
44259         var ctx = this.canvasElCtx();
44260         var widthDelta = curve.endWidth - curve.startWidth;
44261         var drawSteps = Math.floor(curve.length()) * 2;
44262         ctx.beginPath();
44263         ctx.fillStyle = color;
44264         for (var i = 0; i < drawSteps; i += 1) {
44265         var t = i / drawSteps;
44266         var tt = t * t;
44267         var ttt = tt * t;
44268         var u = 1 - t;
44269         var uu = u * u;
44270         var uuu = uu * u;
44271         var x = uuu * curve.startPoint.x;
44272         x += 3 * uu * t * curve.control1.x;
44273         x += 3 * u * tt * curve.control2.x;
44274         x += ttt * curve.endPoint.x;
44275         var y = uuu * curve.startPoint.y;
44276         y += 3 * uu * t * curve.control1.y;
44277         y += 3 * u * tt * curve.control2.y;
44278         y += ttt * curve.endPoint.y;
44279         var width = curve.startWidth + ttt * widthDelta;
44280         this.drawCurveSegment(x, y, width);
44281         }
44282         ctx.closePath();
44283         ctx.fill();
44284     },
44285     
44286     drawCurveSegment: function (x, y, width) {
44287         var ctx = this.canvasElCtx();
44288         ctx.moveTo(x, y);
44289         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44290         this.is_empty = false;
44291     },
44292     
44293     clear: function()
44294     {
44295         var ctx = this.canvasElCtx();
44296         var canvas = this.canvasEl().dom;
44297         ctx.fillStyle = this.bg_color;
44298         ctx.clearRect(0, 0, canvas.width, canvas.height);
44299         ctx.fillRect(0, 0, canvas.width, canvas.height);
44300         this.curve_data = [];
44301         this.reset();
44302         this.is_empty = true;
44303     },
44304     
44305     fileEl: function()
44306     {
44307         return  this.el.select('input',true).first();
44308     },
44309     
44310     canvasEl: function()
44311     {
44312         return this.el.select('canvas',true).first();
44313     },
44314     
44315     canvasElCtx: function()
44316     {
44317         return this.el.select('canvas',true).first().dom.getContext('2d');
44318     },
44319     
44320     getImage: function(type)
44321     {
44322         if(this.is_empty) {
44323             return false;
44324         }
44325         
44326         // encryption ?
44327         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44328     },
44329     
44330     drawFromImage: function(img_src)
44331     {
44332         var img = new Image();
44333         
44334         img.onload = function(){
44335             this.canvasElCtx().drawImage(img, 0, 0);
44336         }.bind(this);
44337         
44338         img.src = img_src;
44339         
44340         this.is_empty = false;
44341     },
44342     
44343     selectImage: function()
44344     {
44345         this.fileEl().dom.click();
44346     },
44347     
44348     uploadImage: function(e)
44349     {
44350         var reader = new FileReader();
44351         
44352         reader.onload = function(e){
44353             var img = new Image();
44354             img.onload = function(){
44355                 this.reset();
44356                 this.canvasElCtx().drawImage(img, 0, 0);
44357             }.bind(this);
44358             img.src = e.target.result;
44359         }.bind(this);
44360         
44361         reader.readAsDataURL(e.target.files[0]);
44362     },
44363     
44364     // Bezier Point Constructor
44365     Point: (function () {
44366         function Point(x, y, time) {
44367             this.x = x;
44368             this.y = y;
44369             this.time = time || Date.now();
44370         }
44371         Point.prototype.distanceTo = function (start) {
44372             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44373         };
44374         Point.prototype.equals = function (other) {
44375             return this.x === other.x && this.y === other.y && this.time === other.time;
44376         };
44377         Point.prototype.velocityFrom = function (start) {
44378             return this.time !== start.time
44379             ? this.distanceTo(start) / (this.time - start.time)
44380             : 0;
44381         };
44382         return Point;
44383     }()),
44384     
44385     
44386     // Bezier Constructor
44387     Bezier: (function () {
44388         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44389             this.startPoint = startPoint;
44390             this.control2 = control2;
44391             this.control1 = control1;
44392             this.endPoint = endPoint;
44393             this.startWidth = startWidth;
44394             this.endWidth = endWidth;
44395         }
44396         Bezier.fromPoints = function (points, widths, scope) {
44397             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44398             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44399             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44400         };
44401         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44402             var dx1 = s1.x - s2.x;
44403             var dy1 = s1.y - s2.y;
44404             var dx2 = s2.x - s3.x;
44405             var dy2 = s2.y - s3.y;
44406             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44407             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44408             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44409             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44410             var dxm = m1.x - m2.x;
44411             var dym = m1.y - m2.y;
44412             var k = l2 / (l1 + l2);
44413             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44414             var tx = s2.x - cm.x;
44415             var ty = s2.y - cm.y;
44416             return {
44417                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44418                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44419             };
44420         };
44421         Bezier.prototype.length = function () {
44422             var steps = 10;
44423             var length = 0;
44424             var px;
44425             var py;
44426             for (var i = 0; i <= steps; i += 1) {
44427                 var t = i / steps;
44428                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44429                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44430                 if (i > 0) {
44431                     var xdiff = cx - px;
44432                     var ydiff = cy - py;
44433                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44434                 }
44435                 px = cx;
44436                 py = cy;
44437             }
44438             return length;
44439         };
44440         Bezier.prototype.point = function (t, start, c1, c2, end) {
44441             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44442             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44443             + (3.0 * c2 * (1.0 - t) * t * t)
44444             + (end * t * t * t);
44445         };
44446         return Bezier;
44447     }()),
44448     
44449     throttleStroke: function(fn, wait) {
44450       if (wait === void 0) { wait = 250; }
44451       var previous = 0;
44452       var timeout = null;
44453       var result;
44454       var storedContext;
44455       var storedArgs;
44456       var later = function () {
44457           previous = Date.now();
44458           timeout = null;
44459           result = fn.apply(storedContext, storedArgs);
44460           if (!timeout) {
44461               storedContext = null;
44462               storedArgs = [];
44463           }
44464       };
44465       return function wrapper() {
44466           var args = [];
44467           for (var _i = 0; _i < arguments.length; _i++) {
44468               args[_i] = arguments[_i];
44469           }
44470           var now = Date.now();
44471           var remaining = wait - (now - previous);
44472           storedContext = this;
44473           storedArgs = args;
44474           if (remaining <= 0 || remaining > wait) {
44475               if (timeout) {
44476                   clearTimeout(timeout);
44477                   timeout = null;
44478               }
44479               previous = now;
44480               result = fn.apply(storedContext, storedArgs);
44481               if (!timeout) {
44482                   storedContext = null;
44483                   storedArgs = [];
44484               }
44485           }
44486           else if (!timeout) {
44487               timeout = window.setTimeout(later, remaining);
44488           }
44489           return result;
44490       };
44491   }
44492   
44493 });
44494
44495  
44496
44497