f6d0dbb66319ec49f651186714156e371a49b08c
[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                     name : 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             srcfile : file,
12664             title : file.name,
12665             mimetype : file.type,
12666             preview : false,
12667             is_deleted : 0
12668         });
12669         
12670     },
12671     
12672     addCard : function (data)
12673     {
12674         // hidden input element?
12675         // if the file is not an image...
12676         //then we need to use something other that and header_image
12677         var t = this;
12678         //   remove.....
12679         var footer = [
12680             {
12681                 xns : Roo.bootstrap,
12682                 xtype : 'CardFooter',
12683                 items: [
12684                     {
12685                         xns : Roo.bootstrap,
12686                         xtype : 'Element',
12687                         cls : 'd-flex',
12688                         items : [
12689                             
12690                             {
12691                                 xns : Roo.bootstrap,
12692                                 xtype : 'Button',
12693                                 html : String.format("<small>{0}</small>", data.title),
12694                                 cls : 'col-11 text-left',
12695                                 size: 'sm',
12696                                 weight: 'link',
12697                                 fa : 'download',
12698                                 listeners : {
12699                                     click : function() {
12700                                         this.downloadCard(data.id)
12701                                     }
12702                                 }
12703                             },
12704                           
12705                             {
12706                                 xns : Roo.bootstrap,
12707                                 xtype : 'Button',
12708                                 
12709                                 size : 'sm',
12710                                 weight: 'danger',
12711                                 cls : 'col-1',
12712                                 fa : 'times',
12713                                 listeners : {
12714                                     click : function() {
12715                                         t.removeCard(data.id)
12716                                     }
12717                                 }
12718                             }
12719                         ]
12720                     }
12721                     
12722                 ] 
12723             }
12724             
12725         ];
12726         
12727         var cn = this.addxtype(
12728             {
12729                  
12730                 xns : Roo.bootstrap,
12731                 xtype : 'Card',
12732                 closeable : true,
12733                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12734                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
12735                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
12736                 data : data,
12737                 html : false,
12738                  
12739                 items : footer,
12740                 initEvents : function() {
12741                     Roo.bootstrap.Card.prototype.initEvents.call(this);
12742                     this.imgEl = this.el.select('.card-img-top').first();
12743                     if (this.imgEl) {
12744                         this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12745                         this.imgEl.set({ 'pointer' : 'cursor' });
12746                                   
12747                     }
12748                     
12749                   
12750                 }
12751                 
12752             }
12753         );
12754         // dont' really need ot update items.
12755         // this.items.push(cn);
12756         this.fileCollection.add(cn);
12757         
12758         if (!data.srcfile) {
12759             this.updateInput();
12760             return;
12761         }
12762             
12763         var _t = this;
12764         var reader = new FileReader();
12765         reader.addEventListener("load", function() {  
12766             data.srcdata =  reader.result;
12767             _t.updateInput();
12768         });
12769         reader.readAsDataURL(data.srcfile);
12770         
12771         
12772         
12773     },
12774     removeCard : function(id)
12775     {
12776         
12777         var card  = this.fileCollection.get(id);
12778         card.data.is_deleted = 1;
12779         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12780         //this.fileCollection.remove(card);
12781         //this.items = this.items.filter(function(e) { return e != card });
12782         // dont' really need ot update items.
12783         card.el.dom.parentNode.removeChild(card.el.dom);
12784         this.updateInput();
12785
12786         
12787     },
12788     reset: function()
12789     {
12790         this.fileCollection.each(function(card) {
12791             if (card.el.dom && card.el.dom.parentNode) {
12792                 card.el.dom.parentNode.removeChild(card.el.dom);
12793             }
12794         });
12795         this.fileCollection.clear();
12796         this.updateInput();
12797     },
12798     
12799     updateInput : function()
12800     {
12801          var data = [];
12802         this.fileCollection.each(function(e) {
12803             data.push(e.data);
12804             
12805         });
12806         this.inputEl().dom.value = JSON.stringify(data);
12807         
12808         
12809         
12810     }
12811     
12812     
12813 });
12814
12815
12816 Roo.bootstrap.CardUploader.ID = -1;/*
12817  * Based on:
12818  * Ext JS Library 1.1.1
12819  * Copyright(c) 2006-2007, Ext JS, LLC.
12820  *
12821  * Originally Released Under LGPL - original licence link has changed is not relivant.
12822  *
12823  * Fork - LGPL
12824  * <script type="text/javascript">
12825  */
12826
12827
12828 /**
12829  * @class Roo.data.SortTypes
12830  * @singleton
12831  * Defines the default sorting (casting?) comparison functions used when sorting data.
12832  */
12833 Roo.data.SortTypes = {
12834     /**
12835      * Default sort that does nothing
12836      * @param {Mixed} s The value being converted
12837      * @return {Mixed} The comparison value
12838      */
12839     none : function(s){
12840         return s;
12841     },
12842     
12843     /**
12844      * The regular expression used to strip tags
12845      * @type {RegExp}
12846      * @property
12847      */
12848     stripTagsRE : /<\/?[^>]+>/gi,
12849     
12850     /**
12851      * Strips all HTML tags to sort on text only
12852      * @param {Mixed} s The value being converted
12853      * @return {String} The comparison value
12854      */
12855     asText : function(s){
12856         return String(s).replace(this.stripTagsRE, "");
12857     },
12858     
12859     /**
12860      * Strips all HTML tags to sort on text only - Case insensitive
12861      * @param {Mixed} s The value being converted
12862      * @return {String} The comparison value
12863      */
12864     asUCText : function(s){
12865         return String(s).toUpperCase().replace(this.stripTagsRE, "");
12866     },
12867     
12868     /**
12869      * Case insensitive string
12870      * @param {Mixed} s The value being converted
12871      * @return {String} The comparison value
12872      */
12873     asUCString : function(s) {
12874         return String(s).toUpperCase();
12875     },
12876     
12877     /**
12878      * Date sorting
12879      * @param {Mixed} s The value being converted
12880      * @return {Number} The comparison value
12881      */
12882     asDate : function(s) {
12883         if(!s){
12884             return 0;
12885         }
12886         if(s instanceof Date){
12887             return s.getTime();
12888         }
12889         return Date.parse(String(s));
12890     },
12891     
12892     /**
12893      * Float sorting
12894      * @param {Mixed} s The value being converted
12895      * @return {Float} The comparison value
12896      */
12897     asFloat : function(s) {
12898         var val = parseFloat(String(s).replace(/,/g, ""));
12899         if(isNaN(val)) {
12900             val = 0;
12901         }
12902         return val;
12903     },
12904     
12905     /**
12906      * Integer sorting
12907      * @param {Mixed} s The value being converted
12908      * @return {Number} The comparison value
12909      */
12910     asInt : function(s) {
12911         var val = parseInt(String(s).replace(/,/g, ""));
12912         if(isNaN(val)) {
12913             val = 0;
12914         }
12915         return val;
12916     }
12917 };/*
12918  * Based on:
12919  * Ext JS Library 1.1.1
12920  * Copyright(c) 2006-2007, Ext JS, LLC.
12921  *
12922  * Originally Released Under LGPL - original licence link has changed is not relivant.
12923  *
12924  * Fork - LGPL
12925  * <script type="text/javascript">
12926  */
12927
12928 /**
12929 * @class Roo.data.Record
12930  * Instances of this class encapsulate both record <em>definition</em> information, and record
12931  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12932  * to access Records cached in an {@link Roo.data.Store} object.<br>
12933  * <p>
12934  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12935  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12936  * objects.<br>
12937  * <p>
12938  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12939  * @constructor
12940  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12941  * {@link #create}. The parameters are the same.
12942  * @param {Array} data An associative Array of data values keyed by the field name.
12943  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12944  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12945  * not specified an integer id is generated.
12946  */
12947 Roo.data.Record = function(data, id){
12948     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12949     this.data = data;
12950 };
12951
12952 /**
12953  * Generate a constructor for a specific record layout.
12954  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12955  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12956  * Each field definition object may contain the following properties: <ul>
12957  * <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,
12958  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12959  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12960  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12961  * is being used, then this is a string containing the javascript expression to reference the data relative to 
12962  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12963  * to the data item relative to the record element. If the mapping expression is the same as the field name,
12964  * this may be omitted.</p></li>
12965  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12966  * <ul><li>auto (Default, implies no conversion)</li>
12967  * <li>string</li>
12968  * <li>int</li>
12969  * <li>float</li>
12970  * <li>boolean</li>
12971  * <li>date</li></ul></p></li>
12972  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12973  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12974  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12975  * by the Reader into an object that will be stored in the Record. It is passed the
12976  * following parameters:<ul>
12977  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12978  * </ul></p></li>
12979  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12980  * </ul>
12981  * <br>usage:<br><pre><code>
12982 var TopicRecord = Roo.data.Record.create(
12983     {name: 'title', mapping: 'topic_title'},
12984     {name: 'author', mapping: 'username'},
12985     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12986     {name: 'lastPost', mapping: 'post_time', type: 'date'},
12987     {name: 'lastPoster', mapping: 'user2'},
12988     {name: 'excerpt', mapping: 'post_text'}
12989 );
12990
12991 var myNewRecord = new TopicRecord({
12992     title: 'Do my job please',
12993     author: 'noobie',
12994     totalPosts: 1,
12995     lastPost: new Date(),
12996     lastPoster: 'Animal',
12997     excerpt: 'No way dude!'
12998 });
12999 myStore.add(myNewRecord);
13000 </code></pre>
13001  * @method create
13002  * @static
13003  */
13004 Roo.data.Record.create = function(o){
13005     var f = function(){
13006         f.superclass.constructor.apply(this, arguments);
13007     };
13008     Roo.extend(f, Roo.data.Record);
13009     var p = f.prototype;
13010     p.fields = new Roo.util.MixedCollection(false, function(field){
13011         return field.name;
13012     });
13013     for(var i = 0, len = o.length; i < len; i++){
13014         p.fields.add(new Roo.data.Field(o[i]));
13015     }
13016     f.getField = function(name){
13017         return p.fields.get(name);  
13018     };
13019     return f;
13020 };
13021
13022 Roo.data.Record.AUTO_ID = 1000;
13023 Roo.data.Record.EDIT = 'edit';
13024 Roo.data.Record.REJECT = 'reject';
13025 Roo.data.Record.COMMIT = 'commit';
13026
13027 Roo.data.Record.prototype = {
13028     /**
13029      * Readonly flag - true if this record has been modified.
13030      * @type Boolean
13031      */
13032     dirty : false,
13033     editing : false,
13034     error: null,
13035     modified: null,
13036
13037     // private
13038     join : function(store){
13039         this.store = store;
13040     },
13041
13042     /**
13043      * Set the named field to the specified value.
13044      * @param {String} name The name of the field to set.
13045      * @param {Object} value The value to set the field to.
13046      */
13047     set : function(name, value){
13048         if(this.data[name] == value){
13049             return;
13050         }
13051         this.dirty = true;
13052         if(!this.modified){
13053             this.modified = {};
13054         }
13055         if(typeof this.modified[name] == 'undefined'){
13056             this.modified[name] = this.data[name];
13057         }
13058         this.data[name] = value;
13059         if(!this.editing && this.store){
13060             this.store.afterEdit(this);
13061         }       
13062     },
13063
13064     /**
13065      * Get the value of the named field.
13066      * @param {String} name The name of the field to get the value of.
13067      * @return {Object} The value of the field.
13068      */
13069     get : function(name){
13070         return this.data[name]; 
13071     },
13072
13073     // private
13074     beginEdit : function(){
13075         this.editing = true;
13076         this.modified = {}; 
13077     },
13078
13079     // private
13080     cancelEdit : function(){
13081         this.editing = false;
13082         delete this.modified;
13083     },
13084
13085     // private
13086     endEdit : function(){
13087         this.editing = false;
13088         if(this.dirty && this.store){
13089             this.store.afterEdit(this);
13090         }
13091     },
13092
13093     /**
13094      * Usually called by the {@link Roo.data.Store} which owns the Record.
13095      * Rejects all changes made to the Record since either creation, or the last commit operation.
13096      * Modified fields are reverted to their original values.
13097      * <p>
13098      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13099      * of reject operations.
13100      */
13101     reject : function(){
13102         var m = this.modified;
13103         for(var n in m){
13104             if(typeof m[n] != "function"){
13105                 this.data[n] = m[n];
13106             }
13107         }
13108         this.dirty = false;
13109         delete this.modified;
13110         this.editing = false;
13111         if(this.store){
13112             this.store.afterReject(this);
13113         }
13114     },
13115
13116     /**
13117      * Usually called by the {@link Roo.data.Store} which owns the Record.
13118      * Commits all changes made to the Record since either creation, or the last commit operation.
13119      * <p>
13120      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13121      * of commit operations.
13122      */
13123     commit : function(){
13124         this.dirty = false;
13125         delete this.modified;
13126         this.editing = false;
13127         if(this.store){
13128             this.store.afterCommit(this);
13129         }
13130     },
13131
13132     // private
13133     hasError : function(){
13134         return this.error != null;
13135     },
13136
13137     // private
13138     clearError : function(){
13139         this.error = null;
13140     },
13141
13142     /**
13143      * Creates a copy of this record.
13144      * @param {String} id (optional) A new record id if you don't want to use this record's id
13145      * @return {Record}
13146      */
13147     copy : function(newId) {
13148         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13149     }
13150 };/*
13151  * Based on:
13152  * Ext JS Library 1.1.1
13153  * Copyright(c) 2006-2007, Ext JS, LLC.
13154  *
13155  * Originally Released Under LGPL - original licence link has changed is not relivant.
13156  *
13157  * Fork - LGPL
13158  * <script type="text/javascript">
13159  */
13160
13161
13162
13163 /**
13164  * @class Roo.data.Store
13165  * @extends Roo.util.Observable
13166  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13167  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13168  * <p>
13169  * 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
13170  * has no knowledge of the format of the data returned by the Proxy.<br>
13171  * <p>
13172  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13173  * instances from the data object. These records are cached and made available through accessor functions.
13174  * @constructor
13175  * Creates a new Store.
13176  * @param {Object} config A config object containing the objects needed for the Store to access data,
13177  * and read the data into Records.
13178  */
13179 Roo.data.Store = function(config){
13180     this.data = new Roo.util.MixedCollection(false);
13181     this.data.getKey = function(o){
13182         return o.id;
13183     };
13184     this.baseParams = {};
13185     // private
13186     this.paramNames = {
13187         "start" : "start",
13188         "limit" : "limit",
13189         "sort" : "sort",
13190         "dir" : "dir",
13191         "multisort" : "_multisort"
13192     };
13193
13194     if(config && config.data){
13195         this.inlineData = config.data;
13196         delete config.data;
13197     }
13198
13199     Roo.apply(this, config);
13200     
13201     if(this.reader){ // reader passed
13202         this.reader = Roo.factory(this.reader, Roo.data);
13203         this.reader.xmodule = this.xmodule || false;
13204         if(!this.recordType){
13205             this.recordType = this.reader.recordType;
13206         }
13207         if(this.reader.onMetaChange){
13208             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13209         }
13210     }
13211
13212     if(this.recordType){
13213         this.fields = this.recordType.prototype.fields;
13214     }
13215     this.modified = [];
13216
13217     this.addEvents({
13218         /**
13219          * @event datachanged
13220          * Fires when the data cache has changed, and a widget which is using this Store
13221          * as a Record cache should refresh its view.
13222          * @param {Store} this
13223          */
13224         datachanged : true,
13225         /**
13226          * @event metachange
13227          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13228          * @param {Store} this
13229          * @param {Object} meta The JSON metadata
13230          */
13231         metachange : true,
13232         /**
13233          * @event add
13234          * Fires when Records have been added to the Store
13235          * @param {Store} this
13236          * @param {Roo.data.Record[]} records The array of Records added
13237          * @param {Number} index The index at which the record(s) were added
13238          */
13239         add : true,
13240         /**
13241          * @event remove
13242          * Fires when a Record has been removed from the Store
13243          * @param {Store} this
13244          * @param {Roo.data.Record} record The Record that was removed
13245          * @param {Number} index The index at which the record was removed
13246          */
13247         remove : true,
13248         /**
13249          * @event update
13250          * Fires when a Record has been updated
13251          * @param {Store} this
13252          * @param {Roo.data.Record} record The Record that was updated
13253          * @param {String} operation The update operation being performed.  Value may be one of:
13254          * <pre><code>
13255  Roo.data.Record.EDIT
13256  Roo.data.Record.REJECT
13257  Roo.data.Record.COMMIT
13258          * </code></pre>
13259          */
13260         update : true,
13261         /**
13262          * @event clear
13263          * Fires when the data cache has been cleared.
13264          * @param {Store} this
13265          */
13266         clear : true,
13267         /**
13268          * @event beforeload
13269          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13270          * the load action will be canceled.
13271          * @param {Store} this
13272          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13273          */
13274         beforeload : true,
13275         /**
13276          * @event beforeloadadd
13277          * Fires after a new set of Records has been loaded.
13278          * @param {Store} this
13279          * @param {Roo.data.Record[]} records The Records that were loaded
13280          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13281          */
13282         beforeloadadd : true,
13283         /**
13284          * @event load
13285          * Fires after a new set of Records has been loaded, before they are added to the store.
13286          * @param {Store} this
13287          * @param {Roo.data.Record[]} records The Records that were loaded
13288          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13289          * @params {Object} return from reader
13290          */
13291         load : true,
13292         /**
13293          * @event loadexception
13294          * Fires if an exception occurs in the Proxy during loading.
13295          * Called with the signature of the Proxy's "loadexception" event.
13296          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13297          * 
13298          * @param {Proxy} 
13299          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13300          * @param {Object} load options 
13301          * @param {Object} jsonData from your request (normally this contains the Exception)
13302          */
13303         loadexception : true
13304     });
13305     
13306     if(this.proxy){
13307         this.proxy = Roo.factory(this.proxy, Roo.data);
13308         this.proxy.xmodule = this.xmodule || false;
13309         this.relayEvents(this.proxy,  ["loadexception"]);
13310     }
13311     this.sortToggle = {};
13312     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13313
13314     Roo.data.Store.superclass.constructor.call(this);
13315
13316     if(this.inlineData){
13317         this.loadData(this.inlineData);
13318         delete this.inlineData;
13319     }
13320 };
13321
13322 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13323      /**
13324     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13325     * without a remote query - used by combo/forms at present.
13326     */
13327     
13328     /**
13329     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13330     */
13331     /**
13332     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13333     */
13334     /**
13335     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13336     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13337     */
13338     /**
13339     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13340     * on any HTTP request
13341     */
13342     /**
13343     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13344     */
13345     /**
13346     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13347     */
13348     multiSort: false,
13349     /**
13350     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13351     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13352     */
13353     remoteSort : false,
13354
13355     /**
13356     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13357      * loaded or when a record is removed. (defaults to false).
13358     */
13359     pruneModifiedRecords : false,
13360
13361     // private
13362     lastOptions : null,
13363
13364     /**
13365      * Add Records to the Store and fires the add event.
13366      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13367      */
13368     add : function(records){
13369         records = [].concat(records);
13370         for(var i = 0, len = records.length; i < len; i++){
13371             records[i].join(this);
13372         }
13373         var index = this.data.length;
13374         this.data.addAll(records);
13375         this.fireEvent("add", this, records, index);
13376     },
13377
13378     /**
13379      * Remove a Record from the Store and fires the remove event.
13380      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13381      */
13382     remove : function(record){
13383         var index = this.data.indexOf(record);
13384         this.data.removeAt(index);
13385  
13386         if(this.pruneModifiedRecords){
13387             this.modified.remove(record);
13388         }
13389         this.fireEvent("remove", this, record, index);
13390     },
13391
13392     /**
13393      * Remove all Records from the Store and fires the clear event.
13394      */
13395     removeAll : function(){
13396         this.data.clear();
13397         if(this.pruneModifiedRecords){
13398             this.modified = [];
13399         }
13400         this.fireEvent("clear", this);
13401     },
13402
13403     /**
13404      * Inserts Records to the Store at the given index and fires the add event.
13405      * @param {Number} index The start index at which to insert the passed Records.
13406      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13407      */
13408     insert : function(index, records){
13409         records = [].concat(records);
13410         for(var i = 0, len = records.length; i < len; i++){
13411             this.data.insert(index, records[i]);
13412             records[i].join(this);
13413         }
13414         this.fireEvent("add", this, records, index);
13415     },
13416
13417     /**
13418      * Get the index within the cache of the passed Record.
13419      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13420      * @return {Number} The index of the passed Record. Returns -1 if not found.
13421      */
13422     indexOf : function(record){
13423         return this.data.indexOf(record);
13424     },
13425
13426     /**
13427      * Get the index within the cache of the Record with the passed id.
13428      * @param {String} id The id of the Record to find.
13429      * @return {Number} The index of the Record. Returns -1 if not found.
13430      */
13431     indexOfId : function(id){
13432         return this.data.indexOfKey(id);
13433     },
13434
13435     /**
13436      * Get the Record with the specified id.
13437      * @param {String} id The id of the Record to find.
13438      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13439      */
13440     getById : function(id){
13441         return this.data.key(id);
13442     },
13443
13444     /**
13445      * Get the Record at the specified index.
13446      * @param {Number} index The index of the Record to find.
13447      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13448      */
13449     getAt : function(index){
13450         return this.data.itemAt(index);
13451     },
13452
13453     /**
13454      * Returns a range of Records between specified indices.
13455      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13456      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13457      * @return {Roo.data.Record[]} An array of Records
13458      */
13459     getRange : function(start, end){
13460         return this.data.getRange(start, end);
13461     },
13462
13463     // private
13464     storeOptions : function(o){
13465         o = Roo.apply({}, o);
13466         delete o.callback;
13467         delete o.scope;
13468         this.lastOptions = o;
13469     },
13470
13471     /**
13472      * Loads the Record cache from the configured Proxy using the configured Reader.
13473      * <p>
13474      * If using remote paging, then the first load call must specify the <em>start</em>
13475      * and <em>limit</em> properties in the options.params property to establish the initial
13476      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13477      * <p>
13478      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13479      * and this call will return before the new data has been loaded. Perform any post-processing
13480      * in a callback function, or in a "load" event handler.</strong>
13481      * <p>
13482      * @param {Object} options An object containing properties which control loading options:<ul>
13483      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13484      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13485      * passed the following arguments:<ul>
13486      * <li>r : Roo.data.Record[]</li>
13487      * <li>options: Options object from the load call</li>
13488      * <li>success: Boolean success indicator</li></ul></li>
13489      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13490      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13491      * </ul>
13492      */
13493     load : function(options){
13494         options = options || {};
13495         if(this.fireEvent("beforeload", this, options) !== false){
13496             this.storeOptions(options);
13497             var p = Roo.apply(options.params || {}, this.baseParams);
13498             // if meta was not loaded from remote source.. try requesting it.
13499             if (!this.reader.metaFromRemote) {
13500                 p._requestMeta = 1;
13501             }
13502             if(this.sortInfo && this.remoteSort){
13503                 var pn = this.paramNames;
13504                 p[pn["sort"]] = this.sortInfo.field;
13505                 p[pn["dir"]] = this.sortInfo.direction;
13506             }
13507             if (this.multiSort) {
13508                 var pn = this.paramNames;
13509                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13510             }
13511             
13512             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13513         }
13514     },
13515
13516     /**
13517      * Reloads the Record cache from the configured Proxy using the configured Reader and
13518      * the options from the last load operation performed.
13519      * @param {Object} options (optional) An object containing properties which may override the options
13520      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13521      * the most recently used options are reused).
13522      */
13523     reload : function(options){
13524         this.load(Roo.applyIf(options||{}, this.lastOptions));
13525     },
13526
13527     // private
13528     // Called as a callback by the Reader during a load operation.
13529     loadRecords : function(o, options, success){
13530         if(!o || success === false){
13531             if(success !== false){
13532                 this.fireEvent("load", this, [], options, o);
13533             }
13534             if(options.callback){
13535                 options.callback.call(options.scope || this, [], options, false);
13536             }
13537             return;
13538         }
13539         // if data returned failure - throw an exception.
13540         if (o.success === false) {
13541             // show a message if no listener is registered.
13542             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13543                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13544             }
13545             // loadmask wil be hooked into this..
13546             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13547             return;
13548         }
13549         var r = o.records, t = o.totalRecords || r.length;
13550         
13551         this.fireEvent("beforeloadadd", this, r, options, o);
13552         
13553         if(!options || options.add !== true){
13554             if(this.pruneModifiedRecords){
13555                 this.modified = [];
13556             }
13557             for(var i = 0, len = r.length; i < len; i++){
13558                 r[i].join(this);
13559             }
13560             if(this.snapshot){
13561                 this.data = this.snapshot;
13562                 delete this.snapshot;
13563             }
13564             this.data.clear();
13565             this.data.addAll(r);
13566             this.totalLength = t;
13567             this.applySort();
13568             this.fireEvent("datachanged", this);
13569         }else{
13570             this.totalLength = Math.max(t, this.data.length+r.length);
13571             this.add(r);
13572         }
13573         
13574         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13575                 
13576             var e = new Roo.data.Record({});
13577
13578             e.set(this.parent.displayField, this.parent.emptyTitle);
13579             e.set(this.parent.valueField, '');
13580
13581             this.insert(0, e);
13582         }
13583             
13584         this.fireEvent("load", this, r, options, o);
13585         if(options.callback){
13586             options.callback.call(options.scope || this, r, options, true);
13587         }
13588     },
13589
13590
13591     /**
13592      * Loads data from a passed data block. A Reader which understands the format of the data
13593      * must have been configured in the constructor.
13594      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13595      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13596      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13597      */
13598     loadData : function(o, append){
13599         var r = this.reader.readRecords(o);
13600         this.loadRecords(r, {add: append}, true);
13601     },
13602     
13603      /**
13604      * using 'cn' the nested child reader read the child array into it's child stores.
13605      * @param {Object} rec The record with a 'children array
13606      */
13607     loadDataFromChildren : function(rec)
13608     {
13609         this.loadData(this.reader.toLoadData(rec));
13610     },
13611     
13612
13613     /**
13614      * Gets the number of cached records.
13615      * <p>
13616      * <em>If using paging, this may not be the total size of the dataset. If the data object
13617      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13618      * the data set size</em>
13619      */
13620     getCount : function(){
13621         return this.data.length || 0;
13622     },
13623
13624     /**
13625      * Gets the total number of records in the dataset as returned by the server.
13626      * <p>
13627      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13628      * the dataset size</em>
13629      */
13630     getTotalCount : function(){
13631         return this.totalLength || 0;
13632     },
13633
13634     /**
13635      * Returns the sort state of the Store as an object with two properties:
13636      * <pre><code>
13637  field {String} The name of the field by which the Records are sorted
13638  direction {String} The sort order, "ASC" or "DESC"
13639      * </code></pre>
13640      */
13641     getSortState : function(){
13642         return this.sortInfo;
13643     },
13644
13645     // private
13646     applySort : function(){
13647         if(this.sortInfo && !this.remoteSort){
13648             var s = this.sortInfo, f = s.field;
13649             var st = this.fields.get(f).sortType;
13650             var fn = function(r1, r2){
13651                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13652                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13653             };
13654             this.data.sort(s.direction, fn);
13655             if(this.snapshot && this.snapshot != this.data){
13656                 this.snapshot.sort(s.direction, fn);
13657             }
13658         }
13659     },
13660
13661     /**
13662      * Sets the default sort column and order to be used by the next load operation.
13663      * @param {String} fieldName The name of the field to sort by.
13664      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13665      */
13666     setDefaultSort : function(field, dir){
13667         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13668     },
13669
13670     /**
13671      * Sort the Records.
13672      * If remote sorting is used, the sort is performed on the server, and the cache is
13673      * reloaded. If local sorting is used, the cache is sorted internally.
13674      * @param {String} fieldName The name of the field to sort by.
13675      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13676      */
13677     sort : function(fieldName, dir){
13678         var f = this.fields.get(fieldName);
13679         if(!dir){
13680             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13681             
13682             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13683                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13684             }else{
13685                 dir = f.sortDir;
13686             }
13687         }
13688         this.sortToggle[f.name] = dir;
13689         this.sortInfo = {field: f.name, direction: dir};
13690         if(!this.remoteSort){
13691             this.applySort();
13692             this.fireEvent("datachanged", this);
13693         }else{
13694             this.load(this.lastOptions);
13695         }
13696     },
13697
13698     /**
13699      * Calls the specified function for each of the Records in the cache.
13700      * @param {Function} fn The function to call. The Record is passed as the first parameter.
13701      * Returning <em>false</em> aborts and exits the iteration.
13702      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13703      */
13704     each : function(fn, scope){
13705         this.data.each(fn, scope);
13706     },
13707
13708     /**
13709      * Gets all records modified since the last commit.  Modified records are persisted across load operations
13710      * (e.g., during paging).
13711      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13712      */
13713     getModifiedRecords : function(){
13714         return this.modified;
13715     },
13716
13717     // private
13718     createFilterFn : function(property, value, anyMatch){
13719         if(!value.exec){ // not a regex
13720             value = String(value);
13721             if(value.length == 0){
13722                 return false;
13723             }
13724             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13725         }
13726         return function(r){
13727             return value.test(r.data[property]);
13728         };
13729     },
13730
13731     /**
13732      * Sums the value of <i>property</i> for each record between start and end and returns the result.
13733      * @param {String} property A field on your records
13734      * @param {Number} start The record index to start at (defaults to 0)
13735      * @param {Number} end The last record index to include (defaults to length - 1)
13736      * @return {Number} The sum
13737      */
13738     sum : function(property, start, end){
13739         var rs = this.data.items, v = 0;
13740         start = start || 0;
13741         end = (end || end === 0) ? end : rs.length-1;
13742
13743         for(var i = start; i <= end; i++){
13744             v += (rs[i].data[property] || 0);
13745         }
13746         return v;
13747     },
13748
13749     /**
13750      * Filter the records by a specified property.
13751      * @param {String} field A field on your records
13752      * @param {String/RegExp} value Either a string that the field
13753      * should start with or a RegExp to test against the field
13754      * @param {Boolean} anyMatch True to match any part not just the beginning
13755      */
13756     filter : function(property, value, anyMatch){
13757         var fn = this.createFilterFn(property, value, anyMatch);
13758         return fn ? this.filterBy(fn) : this.clearFilter();
13759     },
13760
13761     /**
13762      * Filter by a function. The specified function will be called with each
13763      * record in this data source. If the function returns true the record is included,
13764      * otherwise it is filtered.
13765      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13766      * @param {Object} scope (optional) The scope of the function (defaults to this)
13767      */
13768     filterBy : function(fn, scope){
13769         this.snapshot = this.snapshot || this.data;
13770         this.data = this.queryBy(fn, scope||this);
13771         this.fireEvent("datachanged", this);
13772     },
13773
13774     /**
13775      * Query the records by a specified property.
13776      * @param {String} field A field on your records
13777      * @param {String/RegExp} value Either a string that the field
13778      * should start with or a RegExp to test against the field
13779      * @param {Boolean} anyMatch True to match any part not just the beginning
13780      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13781      */
13782     query : function(property, value, anyMatch){
13783         var fn = this.createFilterFn(property, value, anyMatch);
13784         return fn ? this.queryBy(fn) : this.data.clone();
13785     },
13786
13787     /**
13788      * Query by a function. The specified function will be called with each
13789      * record in this data source. If the function returns true the record is included
13790      * in the results.
13791      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13792      * @param {Object} scope (optional) The scope of the function (defaults to this)
13793       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13794      **/
13795     queryBy : function(fn, scope){
13796         var data = this.snapshot || this.data;
13797         return data.filterBy(fn, scope||this);
13798     },
13799
13800     /**
13801      * Collects unique values for a particular dataIndex from this store.
13802      * @param {String} dataIndex The property to collect
13803      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13804      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13805      * @return {Array} An array of the unique values
13806      **/
13807     collect : function(dataIndex, allowNull, bypassFilter){
13808         var d = (bypassFilter === true && this.snapshot) ?
13809                 this.snapshot.items : this.data.items;
13810         var v, sv, r = [], l = {};
13811         for(var i = 0, len = d.length; i < len; i++){
13812             v = d[i].data[dataIndex];
13813             sv = String(v);
13814             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13815                 l[sv] = true;
13816                 r[r.length] = v;
13817             }
13818         }
13819         return r;
13820     },
13821
13822     /**
13823      * Revert to a view of the Record cache with no filtering applied.
13824      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13825      */
13826     clearFilter : function(suppressEvent){
13827         if(this.snapshot && this.snapshot != this.data){
13828             this.data = this.snapshot;
13829             delete this.snapshot;
13830             if(suppressEvent !== true){
13831                 this.fireEvent("datachanged", this);
13832             }
13833         }
13834     },
13835
13836     // private
13837     afterEdit : function(record){
13838         if(this.modified.indexOf(record) == -1){
13839             this.modified.push(record);
13840         }
13841         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13842     },
13843     
13844     // private
13845     afterReject : function(record){
13846         this.modified.remove(record);
13847         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13848     },
13849
13850     // private
13851     afterCommit : function(record){
13852         this.modified.remove(record);
13853         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13854     },
13855
13856     /**
13857      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13858      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13859      */
13860     commitChanges : function(){
13861         var m = this.modified.slice(0);
13862         this.modified = [];
13863         for(var i = 0, len = m.length; i < len; i++){
13864             m[i].commit();
13865         }
13866     },
13867
13868     /**
13869      * Cancel outstanding changes on all changed records.
13870      */
13871     rejectChanges : function(){
13872         var m = this.modified.slice(0);
13873         this.modified = [];
13874         for(var i = 0, len = m.length; i < len; i++){
13875             m[i].reject();
13876         }
13877     },
13878
13879     onMetaChange : function(meta, rtype, o){
13880         this.recordType = rtype;
13881         this.fields = rtype.prototype.fields;
13882         delete this.snapshot;
13883         this.sortInfo = meta.sortInfo || this.sortInfo;
13884         this.modified = [];
13885         this.fireEvent('metachange', this, this.reader.meta);
13886     },
13887     
13888     moveIndex : function(data, type)
13889     {
13890         var index = this.indexOf(data);
13891         
13892         var newIndex = index + type;
13893         
13894         this.remove(data);
13895         
13896         this.insert(newIndex, data);
13897         
13898     }
13899 });/*
13900  * Based on:
13901  * Ext JS Library 1.1.1
13902  * Copyright(c) 2006-2007, Ext JS, LLC.
13903  *
13904  * Originally Released Under LGPL - original licence link has changed is not relivant.
13905  *
13906  * Fork - LGPL
13907  * <script type="text/javascript">
13908  */
13909
13910 /**
13911  * @class Roo.data.SimpleStore
13912  * @extends Roo.data.Store
13913  * Small helper class to make creating Stores from Array data easier.
13914  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13915  * @cfg {Array} fields An array of field definition objects, or field name strings.
13916  * @cfg {Object} an existing reader (eg. copied from another store)
13917  * @cfg {Array} data The multi-dimensional array of data
13918  * @constructor
13919  * @param {Object} config
13920  */
13921 Roo.data.SimpleStore = function(config)
13922 {
13923     Roo.data.SimpleStore.superclass.constructor.call(this, {
13924         isLocal : true,
13925         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13926                 id: config.id
13927             },
13928             Roo.data.Record.create(config.fields)
13929         ),
13930         proxy : new Roo.data.MemoryProxy(config.data)
13931     });
13932     this.load();
13933 };
13934 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13935  * Based on:
13936  * Ext JS Library 1.1.1
13937  * Copyright(c) 2006-2007, Ext JS, LLC.
13938  *
13939  * Originally Released Under LGPL - original licence link has changed is not relivant.
13940  *
13941  * Fork - LGPL
13942  * <script type="text/javascript">
13943  */
13944
13945 /**
13946 /**
13947  * @extends Roo.data.Store
13948  * @class Roo.data.JsonStore
13949  * Small helper class to make creating Stores for JSON data easier. <br/>
13950 <pre><code>
13951 var store = new Roo.data.JsonStore({
13952     url: 'get-images.php',
13953     root: 'images',
13954     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13955 });
13956 </code></pre>
13957  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13958  * JsonReader and HttpProxy (unless inline data is provided).</b>
13959  * @cfg {Array} fields An array of field definition objects, or field name strings.
13960  * @constructor
13961  * @param {Object} config
13962  */
13963 Roo.data.JsonStore = function(c){
13964     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13965         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13966         reader: new Roo.data.JsonReader(c, c.fields)
13967     }));
13968 };
13969 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13970  * Based on:
13971  * Ext JS Library 1.1.1
13972  * Copyright(c) 2006-2007, Ext JS, LLC.
13973  *
13974  * Originally Released Under LGPL - original licence link has changed is not relivant.
13975  *
13976  * Fork - LGPL
13977  * <script type="text/javascript">
13978  */
13979
13980  
13981 Roo.data.Field = function(config){
13982     if(typeof config == "string"){
13983         config = {name: config};
13984     }
13985     Roo.apply(this, config);
13986     
13987     if(!this.type){
13988         this.type = "auto";
13989     }
13990     
13991     var st = Roo.data.SortTypes;
13992     // named sortTypes are supported, here we look them up
13993     if(typeof this.sortType == "string"){
13994         this.sortType = st[this.sortType];
13995     }
13996     
13997     // set default sortType for strings and dates
13998     if(!this.sortType){
13999         switch(this.type){
14000             case "string":
14001                 this.sortType = st.asUCString;
14002                 break;
14003             case "date":
14004                 this.sortType = st.asDate;
14005                 break;
14006             default:
14007                 this.sortType = st.none;
14008         }
14009     }
14010
14011     // define once
14012     var stripRe = /[\$,%]/g;
14013
14014     // prebuilt conversion function for this field, instead of
14015     // switching every time we're reading a value
14016     if(!this.convert){
14017         var cv, dateFormat = this.dateFormat;
14018         switch(this.type){
14019             case "":
14020             case "auto":
14021             case undefined:
14022                 cv = function(v){ return v; };
14023                 break;
14024             case "string":
14025                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14026                 break;
14027             case "int":
14028                 cv = function(v){
14029                     return v !== undefined && v !== null && v !== '' ?
14030                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14031                     };
14032                 break;
14033             case "float":
14034                 cv = function(v){
14035                     return v !== undefined && v !== null && v !== '' ?
14036                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14037                     };
14038                 break;
14039             case "bool":
14040             case "boolean":
14041                 cv = function(v){ return v === true || v === "true" || v == 1; };
14042                 break;
14043             case "date":
14044                 cv = function(v){
14045                     if(!v){
14046                         return '';
14047                     }
14048                     if(v instanceof Date){
14049                         return v;
14050                     }
14051                     if(dateFormat){
14052                         if(dateFormat == "timestamp"){
14053                             return new Date(v*1000);
14054                         }
14055                         return Date.parseDate(v, dateFormat);
14056                     }
14057                     var parsed = Date.parse(v);
14058                     return parsed ? new Date(parsed) : null;
14059                 };
14060              break;
14061             
14062         }
14063         this.convert = cv;
14064     }
14065 };
14066
14067 Roo.data.Field.prototype = {
14068     dateFormat: null,
14069     defaultValue: "",
14070     mapping: null,
14071     sortType : null,
14072     sortDir : "ASC"
14073 };/*
14074  * Based on:
14075  * Ext JS Library 1.1.1
14076  * Copyright(c) 2006-2007, Ext JS, LLC.
14077  *
14078  * Originally Released Under LGPL - original licence link has changed is not relivant.
14079  *
14080  * Fork - LGPL
14081  * <script type="text/javascript">
14082  */
14083  
14084 // Base class for reading structured data from a data source.  This class is intended to be
14085 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14086
14087 /**
14088  * @class Roo.data.DataReader
14089  * Base class for reading structured data from a data source.  This class is intended to be
14090  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14091  */
14092
14093 Roo.data.DataReader = function(meta, recordType){
14094     
14095     this.meta = meta;
14096     
14097     this.recordType = recordType instanceof Array ? 
14098         Roo.data.Record.create(recordType) : recordType;
14099 };
14100
14101 Roo.data.DataReader.prototype = {
14102     
14103     
14104     readerType : 'Data',
14105      /**
14106      * Create an empty record
14107      * @param {Object} data (optional) - overlay some values
14108      * @return {Roo.data.Record} record created.
14109      */
14110     newRow :  function(d) {
14111         var da =  {};
14112         this.recordType.prototype.fields.each(function(c) {
14113             switch( c.type) {
14114                 case 'int' : da[c.name] = 0; break;
14115                 case 'date' : da[c.name] = new Date(); break;
14116                 case 'float' : da[c.name] = 0.0; break;
14117                 case 'boolean' : da[c.name] = false; break;
14118                 default : da[c.name] = ""; break;
14119             }
14120             
14121         });
14122         return new this.recordType(Roo.apply(da, d));
14123     }
14124     
14125     
14126 };/*
14127  * Based on:
14128  * Ext JS Library 1.1.1
14129  * Copyright(c) 2006-2007, Ext JS, LLC.
14130  *
14131  * Originally Released Under LGPL - original licence link has changed is not relivant.
14132  *
14133  * Fork - LGPL
14134  * <script type="text/javascript">
14135  */
14136
14137 /**
14138  * @class Roo.data.DataProxy
14139  * @extends Roo.data.Observable
14140  * This class is an abstract base class for implementations which provide retrieval of
14141  * unformatted data objects.<br>
14142  * <p>
14143  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14144  * (of the appropriate type which knows how to parse the data object) to provide a block of
14145  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14146  * <p>
14147  * Custom implementations must implement the load method as described in
14148  * {@link Roo.data.HttpProxy#load}.
14149  */
14150 Roo.data.DataProxy = function(){
14151     this.addEvents({
14152         /**
14153          * @event beforeload
14154          * Fires before a network request is made to retrieve a data object.
14155          * @param {Object} This DataProxy object.
14156          * @param {Object} params The params parameter to the load function.
14157          */
14158         beforeload : true,
14159         /**
14160          * @event load
14161          * Fires before the load method's callback is called.
14162          * @param {Object} This DataProxy object.
14163          * @param {Object} o The data object.
14164          * @param {Object} arg The callback argument object passed to the load function.
14165          */
14166         load : true,
14167         /**
14168          * @event loadexception
14169          * Fires if an Exception occurs during data retrieval.
14170          * @param {Object} This DataProxy object.
14171          * @param {Object} o The data object.
14172          * @param {Object} arg The callback argument object passed to the load function.
14173          * @param {Object} e The Exception.
14174          */
14175         loadexception : true
14176     });
14177     Roo.data.DataProxy.superclass.constructor.call(this);
14178 };
14179
14180 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14181
14182     /**
14183      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14184      */
14185 /*
14186  * Based on:
14187  * Ext JS Library 1.1.1
14188  * Copyright(c) 2006-2007, Ext JS, LLC.
14189  *
14190  * Originally Released Under LGPL - original licence link has changed is not relivant.
14191  *
14192  * Fork - LGPL
14193  * <script type="text/javascript">
14194  */
14195 /**
14196  * @class Roo.data.MemoryProxy
14197  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14198  * to the Reader when its load method is called.
14199  * @constructor
14200  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14201  */
14202 Roo.data.MemoryProxy = function(data){
14203     if (data.data) {
14204         data = data.data;
14205     }
14206     Roo.data.MemoryProxy.superclass.constructor.call(this);
14207     this.data = data;
14208 };
14209
14210 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14211     
14212     /**
14213      * Load data from the requested source (in this case an in-memory
14214      * data object passed to the constructor), read the data object into
14215      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14216      * process that block using the passed callback.
14217      * @param {Object} params This parameter is not used by the MemoryProxy class.
14218      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14219      * object into a block of Roo.data.Records.
14220      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14221      * The function must be passed <ul>
14222      * <li>The Record block object</li>
14223      * <li>The "arg" argument from the load function</li>
14224      * <li>A boolean success indicator</li>
14225      * </ul>
14226      * @param {Object} scope The scope in which to call the callback
14227      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14228      */
14229     load : function(params, reader, callback, scope, arg){
14230         params = params || {};
14231         var result;
14232         try {
14233             result = reader.readRecords(params.data ? params.data :this.data);
14234         }catch(e){
14235             this.fireEvent("loadexception", this, arg, null, e);
14236             callback.call(scope, null, arg, false);
14237             return;
14238         }
14239         callback.call(scope, result, arg, true);
14240     },
14241     
14242     // private
14243     update : function(params, records){
14244         
14245     }
14246 });/*
14247  * Based on:
14248  * Ext JS Library 1.1.1
14249  * Copyright(c) 2006-2007, Ext JS, LLC.
14250  *
14251  * Originally Released Under LGPL - original licence link has changed is not relivant.
14252  *
14253  * Fork - LGPL
14254  * <script type="text/javascript">
14255  */
14256 /**
14257  * @class Roo.data.HttpProxy
14258  * @extends Roo.data.DataProxy
14259  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14260  * configured to reference a certain URL.<br><br>
14261  * <p>
14262  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14263  * from which the running page was served.<br><br>
14264  * <p>
14265  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14266  * <p>
14267  * Be aware that to enable the browser to parse an XML document, the server must set
14268  * the Content-Type header in the HTTP response to "text/xml".
14269  * @constructor
14270  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14271  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14272  * will be used to make the request.
14273  */
14274 Roo.data.HttpProxy = function(conn){
14275     Roo.data.HttpProxy.superclass.constructor.call(this);
14276     // is conn a conn config or a real conn?
14277     this.conn = conn;
14278     this.useAjax = !conn || !conn.events;
14279   
14280 };
14281
14282 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14283     // thse are take from connection...
14284     
14285     /**
14286      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14287      */
14288     /**
14289      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14290      * extra parameters to each request made by this object. (defaults to undefined)
14291      */
14292     /**
14293      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14294      *  to each request made by this object. (defaults to undefined)
14295      */
14296     /**
14297      * @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)
14298      */
14299     /**
14300      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14301      */
14302      /**
14303      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14304      * @type Boolean
14305      */
14306   
14307
14308     /**
14309      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14310      * @type Boolean
14311      */
14312     /**
14313      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14314      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14315      * a finer-grained basis than the DataProxy events.
14316      */
14317     getConnection : function(){
14318         return this.useAjax ? Roo.Ajax : this.conn;
14319     },
14320
14321     /**
14322      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14323      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14324      * process that block using the passed callback.
14325      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14326      * for the request to the remote server.
14327      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14328      * object into a block of Roo.data.Records.
14329      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14330      * The function must be passed <ul>
14331      * <li>The Record block object</li>
14332      * <li>The "arg" argument from the load function</li>
14333      * <li>A boolean success indicator</li>
14334      * </ul>
14335      * @param {Object} scope The scope in which to call the callback
14336      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14337      */
14338     load : function(params, reader, callback, scope, arg){
14339         if(this.fireEvent("beforeload", this, params) !== false){
14340             var  o = {
14341                 params : params || {},
14342                 request: {
14343                     callback : callback,
14344                     scope : scope,
14345                     arg : arg
14346                 },
14347                 reader: reader,
14348                 callback : this.loadResponse,
14349                 scope: this
14350             };
14351             if(this.useAjax){
14352                 Roo.applyIf(o, this.conn);
14353                 if(this.activeRequest){
14354                     Roo.Ajax.abort(this.activeRequest);
14355                 }
14356                 this.activeRequest = Roo.Ajax.request(o);
14357             }else{
14358                 this.conn.request(o);
14359             }
14360         }else{
14361             callback.call(scope||this, null, arg, false);
14362         }
14363     },
14364
14365     // private
14366     loadResponse : function(o, success, response){
14367         delete this.activeRequest;
14368         if(!success){
14369             this.fireEvent("loadexception", this, o, response);
14370             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14371             return;
14372         }
14373         var result;
14374         try {
14375             result = o.reader.read(response);
14376         }catch(e){
14377             this.fireEvent("loadexception", this, o, response, e);
14378             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14379             return;
14380         }
14381         
14382         this.fireEvent("load", this, o, o.request.arg);
14383         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14384     },
14385
14386     // private
14387     update : function(dataSet){
14388
14389     },
14390
14391     // private
14392     updateResponse : function(dataSet){
14393
14394     }
14395 });/*
14396  * Based on:
14397  * Ext JS Library 1.1.1
14398  * Copyright(c) 2006-2007, Ext JS, LLC.
14399  *
14400  * Originally Released Under LGPL - original licence link has changed is not relivant.
14401  *
14402  * Fork - LGPL
14403  * <script type="text/javascript">
14404  */
14405
14406 /**
14407  * @class Roo.data.ScriptTagProxy
14408  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14409  * other than the originating domain of the running page.<br><br>
14410  * <p>
14411  * <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
14412  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14413  * <p>
14414  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14415  * source code that is used as the source inside a &lt;script> tag.<br><br>
14416  * <p>
14417  * In order for the browser to process the returned data, the server must wrap the data object
14418  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14419  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14420  * depending on whether the callback name was passed:
14421  * <p>
14422  * <pre><code>
14423 boolean scriptTag = false;
14424 String cb = request.getParameter("callback");
14425 if (cb != null) {
14426     scriptTag = true;
14427     response.setContentType("text/javascript");
14428 } else {
14429     response.setContentType("application/x-json");
14430 }
14431 Writer out = response.getWriter();
14432 if (scriptTag) {
14433     out.write(cb + "(");
14434 }
14435 out.print(dataBlock.toJsonString());
14436 if (scriptTag) {
14437     out.write(");");
14438 }
14439 </pre></code>
14440  *
14441  * @constructor
14442  * @param {Object} config A configuration object.
14443  */
14444 Roo.data.ScriptTagProxy = function(config){
14445     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14446     Roo.apply(this, config);
14447     this.head = document.getElementsByTagName("head")[0];
14448 };
14449
14450 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14451
14452 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14453     /**
14454      * @cfg {String} url The URL from which to request the data object.
14455      */
14456     /**
14457      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14458      */
14459     timeout : 30000,
14460     /**
14461      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14462      * the server the name of the callback function set up by the load call to process the returned data object.
14463      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14464      * javascript output which calls this named function passing the data object as its only parameter.
14465      */
14466     callbackParam : "callback",
14467     /**
14468      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14469      * name to the request.
14470      */
14471     nocache : true,
14472
14473     /**
14474      * Load data from the configured URL, read the data object into
14475      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14476      * process that block using the passed callback.
14477      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14478      * for the request to the remote server.
14479      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14480      * object into a block of Roo.data.Records.
14481      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14482      * The function must be passed <ul>
14483      * <li>The Record block object</li>
14484      * <li>The "arg" argument from the load function</li>
14485      * <li>A boolean success indicator</li>
14486      * </ul>
14487      * @param {Object} scope The scope in which to call the callback
14488      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14489      */
14490     load : function(params, reader, callback, scope, arg){
14491         if(this.fireEvent("beforeload", this, params) !== false){
14492
14493             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14494
14495             var url = this.url;
14496             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14497             if(this.nocache){
14498                 url += "&_dc=" + (new Date().getTime());
14499             }
14500             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14501             var trans = {
14502                 id : transId,
14503                 cb : "stcCallback"+transId,
14504                 scriptId : "stcScript"+transId,
14505                 params : params,
14506                 arg : arg,
14507                 url : url,
14508                 callback : callback,
14509                 scope : scope,
14510                 reader : reader
14511             };
14512             var conn = this;
14513
14514             window[trans.cb] = function(o){
14515                 conn.handleResponse(o, trans);
14516             };
14517
14518             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14519
14520             if(this.autoAbort !== false){
14521                 this.abort();
14522             }
14523
14524             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14525
14526             var script = document.createElement("script");
14527             script.setAttribute("src", url);
14528             script.setAttribute("type", "text/javascript");
14529             script.setAttribute("id", trans.scriptId);
14530             this.head.appendChild(script);
14531
14532             this.trans = trans;
14533         }else{
14534             callback.call(scope||this, null, arg, false);
14535         }
14536     },
14537
14538     // private
14539     isLoading : function(){
14540         return this.trans ? true : false;
14541     },
14542
14543     /**
14544      * Abort the current server request.
14545      */
14546     abort : function(){
14547         if(this.isLoading()){
14548             this.destroyTrans(this.trans);
14549         }
14550     },
14551
14552     // private
14553     destroyTrans : function(trans, isLoaded){
14554         this.head.removeChild(document.getElementById(trans.scriptId));
14555         clearTimeout(trans.timeoutId);
14556         if(isLoaded){
14557             window[trans.cb] = undefined;
14558             try{
14559                 delete window[trans.cb];
14560             }catch(e){}
14561         }else{
14562             // if hasn't been loaded, wait for load to remove it to prevent script error
14563             window[trans.cb] = function(){
14564                 window[trans.cb] = undefined;
14565                 try{
14566                     delete window[trans.cb];
14567                 }catch(e){}
14568             };
14569         }
14570     },
14571
14572     // private
14573     handleResponse : function(o, trans){
14574         this.trans = false;
14575         this.destroyTrans(trans, true);
14576         var result;
14577         try {
14578             result = trans.reader.readRecords(o);
14579         }catch(e){
14580             this.fireEvent("loadexception", this, o, trans.arg, e);
14581             trans.callback.call(trans.scope||window, null, trans.arg, false);
14582             return;
14583         }
14584         this.fireEvent("load", this, o, trans.arg);
14585         trans.callback.call(trans.scope||window, result, trans.arg, true);
14586     },
14587
14588     // private
14589     handleFailure : function(trans){
14590         this.trans = false;
14591         this.destroyTrans(trans, false);
14592         this.fireEvent("loadexception", this, null, trans.arg);
14593         trans.callback.call(trans.scope||window, null, trans.arg, false);
14594     }
14595 });/*
14596  * Based on:
14597  * Ext JS Library 1.1.1
14598  * Copyright(c) 2006-2007, Ext JS, LLC.
14599  *
14600  * Originally Released Under LGPL - original licence link has changed is not relivant.
14601  *
14602  * Fork - LGPL
14603  * <script type="text/javascript">
14604  */
14605
14606 /**
14607  * @class Roo.data.JsonReader
14608  * @extends Roo.data.DataReader
14609  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14610  * based on mappings in a provided Roo.data.Record constructor.
14611  * 
14612  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14613  * in the reply previously. 
14614  * 
14615  * <p>
14616  * Example code:
14617  * <pre><code>
14618 var RecordDef = Roo.data.Record.create([
14619     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14620     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14621 ]);
14622 var myReader = new Roo.data.JsonReader({
14623     totalProperty: "results",    // The property which contains the total dataset size (optional)
14624     root: "rows",                // The property which contains an Array of row objects
14625     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14626 }, RecordDef);
14627 </code></pre>
14628  * <p>
14629  * This would consume a JSON file like this:
14630  * <pre><code>
14631 { 'results': 2, 'rows': [
14632     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14633     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14634 }
14635 </code></pre>
14636  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14637  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14638  * paged from the remote server.
14639  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14640  * @cfg {String} root name of the property which contains the Array of row objects.
14641  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14642  * @cfg {Array} fields Array of field definition objects
14643  * @constructor
14644  * Create a new JsonReader
14645  * @param {Object} meta Metadata configuration options
14646  * @param {Object} recordType Either an Array of field definition objects,
14647  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14648  */
14649 Roo.data.JsonReader = function(meta, recordType){
14650     
14651     meta = meta || {};
14652     // set some defaults:
14653     Roo.applyIf(meta, {
14654         totalProperty: 'total',
14655         successProperty : 'success',
14656         root : 'data',
14657         id : 'id'
14658     });
14659     
14660     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14661 };
14662 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14663     
14664     readerType : 'Json',
14665     
14666     /**
14667      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14668      * Used by Store query builder to append _requestMeta to params.
14669      * 
14670      */
14671     metaFromRemote : false,
14672     /**
14673      * This method is only used by a DataProxy which has retrieved data from a remote server.
14674      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14675      * @return {Object} data A data block which is used by an Roo.data.Store object as
14676      * a cache of Roo.data.Records.
14677      */
14678     read : function(response){
14679         var json = response.responseText;
14680        
14681         var o = /* eval:var:o */ eval("("+json+")");
14682         if(!o) {
14683             throw {message: "JsonReader.read: Json object not found"};
14684         }
14685         
14686         if(o.metaData){
14687             
14688             delete this.ef;
14689             this.metaFromRemote = true;
14690             this.meta = o.metaData;
14691             this.recordType = Roo.data.Record.create(o.metaData.fields);
14692             this.onMetaChange(this.meta, this.recordType, o);
14693         }
14694         return this.readRecords(o);
14695     },
14696
14697     // private function a store will implement
14698     onMetaChange : function(meta, recordType, o){
14699
14700     },
14701
14702     /**
14703          * @ignore
14704          */
14705     simpleAccess: function(obj, subsc) {
14706         return obj[subsc];
14707     },
14708
14709         /**
14710          * @ignore
14711          */
14712     getJsonAccessor: function(){
14713         var re = /[\[\.]/;
14714         return function(expr) {
14715             try {
14716                 return(re.test(expr))
14717                     ? new Function("obj", "return obj." + expr)
14718                     : function(obj){
14719                         return obj[expr];
14720                     };
14721             } catch(e){}
14722             return Roo.emptyFn;
14723         };
14724     }(),
14725
14726     /**
14727      * Create a data block containing Roo.data.Records from an XML document.
14728      * @param {Object} o An object which contains an Array of row objects in the property specified
14729      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14730      * which contains the total size of the dataset.
14731      * @return {Object} data A data block which is used by an Roo.data.Store object as
14732      * a cache of Roo.data.Records.
14733      */
14734     readRecords : function(o){
14735         /**
14736          * After any data loads, the raw JSON data is available for further custom processing.
14737          * @type Object
14738          */
14739         this.o = o;
14740         var s = this.meta, Record = this.recordType,
14741             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14742
14743 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
14744         if (!this.ef) {
14745             if(s.totalProperty) {
14746                     this.getTotal = this.getJsonAccessor(s.totalProperty);
14747                 }
14748                 if(s.successProperty) {
14749                     this.getSuccess = this.getJsonAccessor(s.successProperty);
14750                 }
14751                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14752                 if (s.id) {
14753                         var g = this.getJsonAccessor(s.id);
14754                         this.getId = function(rec) {
14755                                 var r = g(rec);  
14756                                 return (r === undefined || r === "") ? null : r;
14757                         };
14758                 } else {
14759                         this.getId = function(){return null;};
14760                 }
14761             this.ef = [];
14762             for(var jj = 0; jj < fl; jj++){
14763                 f = fi[jj];
14764                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14765                 this.ef[jj] = this.getJsonAccessor(map);
14766             }
14767         }
14768
14769         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14770         if(s.totalProperty){
14771             var vt = parseInt(this.getTotal(o), 10);
14772             if(!isNaN(vt)){
14773                 totalRecords = vt;
14774             }
14775         }
14776         if(s.successProperty){
14777             var vs = this.getSuccess(o);
14778             if(vs === false || vs === 'false'){
14779                 success = false;
14780             }
14781         }
14782         var records = [];
14783         for(var i = 0; i < c; i++){
14784                 var n = root[i];
14785             var values = {};
14786             var id = this.getId(n);
14787             for(var j = 0; j < fl; j++){
14788                 f = fi[j];
14789             var v = this.ef[j](n);
14790             if (!f.convert) {
14791                 Roo.log('missing convert for ' + f.name);
14792                 Roo.log(f);
14793                 continue;
14794             }
14795             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14796             }
14797             var record = new Record(values, id);
14798             record.json = n;
14799             records[i] = record;
14800         }
14801         return {
14802             raw : o,
14803             success : success,
14804             records : records,
14805             totalRecords : totalRecords
14806         };
14807     },
14808     // used when loading children.. @see loadDataFromChildren
14809     toLoadData: function(rec)
14810     {
14811         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14812         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14813         return { data : data, total : data.length };
14814         
14815     }
14816 });/*
14817  * Based on:
14818  * Ext JS Library 1.1.1
14819  * Copyright(c) 2006-2007, Ext JS, LLC.
14820  *
14821  * Originally Released Under LGPL - original licence link has changed is not relivant.
14822  *
14823  * Fork - LGPL
14824  * <script type="text/javascript">
14825  */
14826
14827 /**
14828  * @class Roo.data.ArrayReader
14829  * @extends Roo.data.DataReader
14830  * Data reader class to create an Array of Roo.data.Record objects from an Array.
14831  * Each element of that Array represents a row of data fields. The
14832  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14833  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14834  * <p>
14835  * Example code:.
14836  * <pre><code>
14837 var RecordDef = Roo.data.Record.create([
14838     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
14839     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
14840 ]);
14841 var myReader = new Roo.data.ArrayReader({
14842     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
14843 }, RecordDef);
14844 </code></pre>
14845  * <p>
14846  * This would consume an Array like this:
14847  * <pre><code>
14848 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14849   </code></pre>
14850  
14851  * @constructor
14852  * Create a new JsonReader
14853  * @param {Object} meta Metadata configuration options.
14854  * @param {Object|Array} recordType Either an Array of field definition objects
14855  * 
14856  * @cfg {Array} fields Array of field definition objects
14857  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14858  * as specified to {@link Roo.data.Record#create},
14859  * or an {@link Roo.data.Record} object
14860  *
14861  * 
14862  * created using {@link Roo.data.Record#create}.
14863  */
14864 Roo.data.ArrayReader = function(meta, recordType)
14865 {    
14866     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14867 };
14868
14869 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14870     
14871       /**
14872      * Create a data block containing Roo.data.Records from an XML document.
14873      * @param {Object} o An Array of row objects which represents the dataset.
14874      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14875      * a cache of Roo.data.Records.
14876      */
14877     readRecords : function(o)
14878     {
14879         var sid = this.meta ? this.meta.id : null;
14880         var recordType = this.recordType, fields = recordType.prototype.fields;
14881         var records = [];
14882         var root = o;
14883         for(var i = 0; i < root.length; i++){
14884                 var n = root[i];
14885             var values = {};
14886             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14887             for(var j = 0, jlen = fields.length; j < jlen; j++){
14888                 var f = fields.items[j];
14889                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14890                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14891                 v = f.convert(v);
14892                 values[f.name] = v;
14893             }
14894             var record = new recordType(values, id);
14895             record.json = n;
14896             records[records.length] = record;
14897         }
14898         return {
14899             records : records,
14900             totalRecords : records.length
14901         };
14902     },
14903     // used when loading children.. @see loadDataFromChildren
14904     toLoadData: function(rec)
14905     {
14906         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14907         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14908         
14909     }
14910     
14911     
14912 });/*
14913  * - LGPL
14914  * * 
14915  */
14916
14917 /**
14918  * @class Roo.bootstrap.ComboBox
14919  * @extends Roo.bootstrap.TriggerField
14920  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14921  * @cfg {Boolean} append (true|false) default false
14922  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14923  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14924  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14925  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14926  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14927  * @cfg {Boolean} animate default true
14928  * @cfg {Boolean} emptyResultText only for touch device
14929  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14930  * @cfg {String} emptyTitle default ''
14931  * @cfg {Number} width fixed with? experimental
14932  * @constructor
14933  * Create a new ComboBox.
14934  * @param {Object} config Configuration options
14935  */
14936 Roo.bootstrap.ComboBox = function(config){
14937     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14938     this.addEvents({
14939         /**
14940          * @event expand
14941          * Fires when the dropdown list is expanded
14942         * @param {Roo.bootstrap.ComboBox} combo This combo box
14943         */
14944         'expand' : true,
14945         /**
14946          * @event collapse
14947          * Fires when the dropdown list is collapsed
14948         * @param {Roo.bootstrap.ComboBox} combo This combo box
14949         */
14950         'collapse' : true,
14951         /**
14952          * @event beforeselect
14953          * Fires before a list item is selected. Return false to cancel the selection.
14954         * @param {Roo.bootstrap.ComboBox} combo This combo box
14955         * @param {Roo.data.Record} record The data record returned from the underlying store
14956         * @param {Number} index The index of the selected item in the dropdown list
14957         */
14958         'beforeselect' : true,
14959         /**
14960          * @event select
14961          * Fires when a list item is selected
14962         * @param {Roo.bootstrap.ComboBox} combo This combo box
14963         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14964         * @param {Number} index The index of the selected item in the dropdown list
14965         */
14966         'select' : true,
14967         /**
14968          * @event beforequery
14969          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14970          * The event object passed has these properties:
14971         * @param {Roo.bootstrap.ComboBox} combo This combo box
14972         * @param {String} query The query
14973         * @param {Boolean} forceAll true to force "all" query
14974         * @param {Boolean} cancel true to cancel the query
14975         * @param {Object} e The query event object
14976         */
14977         'beforequery': true,
14978          /**
14979          * @event add
14980          * Fires when the 'add' icon is pressed (add a listener to enable add button)
14981         * @param {Roo.bootstrap.ComboBox} combo This combo box
14982         */
14983         'add' : true,
14984         /**
14985          * @event edit
14986          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14987         * @param {Roo.bootstrap.ComboBox} combo This combo box
14988         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14989         */
14990         'edit' : true,
14991         /**
14992          * @event remove
14993          * Fires when the remove value from the combobox array
14994         * @param {Roo.bootstrap.ComboBox} combo This combo box
14995         */
14996         'remove' : true,
14997         /**
14998          * @event afterremove
14999          * Fires when the remove value from the combobox array
15000         * @param {Roo.bootstrap.ComboBox} combo This combo box
15001         */
15002         'afterremove' : true,
15003         /**
15004          * @event specialfilter
15005          * Fires when specialfilter
15006             * @param {Roo.bootstrap.ComboBox} combo This combo box
15007             */
15008         'specialfilter' : true,
15009         /**
15010          * @event tick
15011          * Fires when tick the element
15012             * @param {Roo.bootstrap.ComboBox} combo This combo box
15013             */
15014         'tick' : true,
15015         /**
15016          * @event touchviewdisplay
15017          * Fires when touch view require special display (default is using displayField)
15018             * @param {Roo.bootstrap.ComboBox} combo This combo box
15019             * @param {Object} cfg set html .
15020             */
15021         'touchviewdisplay' : true
15022         
15023     });
15024     
15025     this.item = [];
15026     this.tickItems = [];
15027     
15028     this.selectedIndex = -1;
15029     if(this.mode == 'local'){
15030         if(config.queryDelay === undefined){
15031             this.queryDelay = 10;
15032         }
15033         if(config.minChars === undefined){
15034             this.minChars = 0;
15035         }
15036     }
15037 };
15038
15039 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15040      
15041     /**
15042      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15043      * rendering into an Roo.Editor, defaults to false)
15044      */
15045     /**
15046      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15047      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15048      */
15049     /**
15050      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15051      */
15052     /**
15053      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15054      * the dropdown list (defaults to undefined, with no header element)
15055      */
15056
15057      /**
15058      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15059      */
15060      
15061      /**
15062      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15063      */
15064     listWidth: undefined,
15065     /**
15066      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15067      * mode = 'remote' or 'text' if mode = 'local')
15068      */
15069     displayField: undefined,
15070     
15071     /**
15072      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15073      * mode = 'remote' or 'value' if mode = 'local'). 
15074      * Note: use of a valueField requires the user make a selection
15075      * in order for a value to be mapped.
15076      */
15077     valueField: undefined,
15078     /**
15079      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15080      */
15081     modalTitle : '',
15082     
15083     /**
15084      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15085      * field's data value (defaults to the underlying DOM element's name)
15086      */
15087     hiddenName: undefined,
15088     /**
15089      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15090      */
15091     listClass: '',
15092     /**
15093      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15094      */
15095     selectedClass: 'active',
15096     
15097     /**
15098      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15099      */
15100     shadow:'sides',
15101     /**
15102      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15103      * anchor positions (defaults to 'tl-bl')
15104      */
15105     listAlign: 'tl-bl?',
15106     /**
15107      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15108      */
15109     maxHeight: 300,
15110     /**
15111      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15112      * query specified by the allQuery config option (defaults to 'query')
15113      */
15114     triggerAction: 'query',
15115     /**
15116      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15117      * (defaults to 4, does not apply if editable = false)
15118      */
15119     minChars : 4,
15120     /**
15121      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15122      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15123      */
15124     typeAhead: false,
15125     /**
15126      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15127      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15128      */
15129     queryDelay: 500,
15130     /**
15131      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15132      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15133      */
15134     pageSize: 0,
15135     /**
15136      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15137      * when editable = true (defaults to false)
15138      */
15139     selectOnFocus:false,
15140     /**
15141      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15142      */
15143     queryParam: 'query',
15144     /**
15145      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15146      * when mode = 'remote' (defaults to 'Loading...')
15147      */
15148     loadingText: 'Loading...',
15149     /**
15150      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15151      */
15152     resizable: false,
15153     /**
15154      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15155      */
15156     handleHeight : 8,
15157     /**
15158      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15159      * traditional select (defaults to true)
15160      */
15161     editable: true,
15162     /**
15163      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15164      */
15165     allQuery: '',
15166     /**
15167      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15168      */
15169     mode: 'remote',
15170     /**
15171      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15172      * listWidth has a higher value)
15173      */
15174     minListWidth : 70,
15175     /**
15176      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15177      * allow the user to set arbitrary text into the field (defaults to false)
15178      */
15179     forceSelection:false,
15180     /**
15181      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15182      * if typeAhead = true (defaults to 250)
15183      */
15184     typeAheadDelay : 250,
15185     /**
15186      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15187      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15188      */
15189     valueNotFoundText : undefined,
15190     /**
15191      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15192      */
15193     blockFocus : false,
15194     
15195     /**
15196      * @cfg {Boolean} disableClear Disable showing of clear button.
15197      */
15198     disableClear : false,
15199     /**
15200      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15201      */
15202     alwaysQuery : false,
15203     
15204     /**
15205      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15206      */
15207     multiple : false,
15208     
15209     /**
15210      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15211      */
15212     invalidClass : "has-warning",
15213     
15214     /**
15215      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15216      */
15217     validClass : "has-success",
15218     
15219     /**
15220      * @cfg {Boolean} specialFilter (true|false) special filter default false
15221      */
15222     specialFilter : false,
15223     
15224     /**
15225      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15226      */
15227     mobileTouchView : true,
15228     
15229     /**
15230      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15231      */
15232     useNativeIOS : false,
15233     
15234     /**
15235      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15236      */
15237     mobile_restrict_height : false,
15238     
15239     ios_options : false,
15240     
15241     //private
15242     addicon : false,
15243     editicon: false,
15244     
15245     page: 0,
15246     hasQuery: false,
15247     append: false,
15248     loadNext: false,
15249     autoFocus : true,
15250     tickable : false,
15251     btnPosition : 'right',
15252     triggerList : true,
15253     showToggleBtn : true,
15254     animate : true,
15255     emptyResultText: 'Empty',
15256     triggerText : 'Select',
15257     emptyTitle : '',
15258     width : false,
15259     
15260     // element that contains real text value.. (when hidden is used..)
15261     
15262     getAutoCreate : function()
15263     {   
15264         var cfg = false;
15265         //render
15266         /*
15267          * Render classic select for iso
15268          */
15269         
15270         if(Roo.isIOS && this.useNativeIOS){
15271             cfg = this.getAutoCreateNativeIOS();
15272             return cfg;
15273         }
15274         
15275         /*
15276          * Touch Devices
15277          */
15278         
15279         if(Roo.isTouch && this.mobileTouchView){
15280             cfg = this.getAutoCreateTouchView();
15281             return cfg;;
15282         }
15283         
15284         /*
15285          *  Normal ComboBox
15286          */
15287         if(!this.tickable){
15288             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15289             return cfg;
15290         }
15291         
15292         /*
15293          *  ComboBox with tickable selections
15294          */
15295              
15296         var align = this.labelAlign || this.parentLabelAlign();
15297         
15298         cfg = {
15299             cls : 'form-group roo-combobox-tickable' //input-group
15300         };
15301         
15302         var btn_text_select = '';
15303         var btn_text_done = '';
15304         var btn_text_cancel = '';
15305         
15306         if (this.btn_text_show) {
15307             btn_text_select = 'Select';
15308             btn_text_done = 'Done';
15309             btn_text_cancel = 'Cancel'; 
15310         }
15311         
15312         var buttons = {
15313             tag : 'div',
15314             cls : 'tickable-buttons',
15315             cn : [
15316                 {
15317                     tag : 'button',
15318                     type : 'button',
15319                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15320                     //html : this.triggerText
15321                     html: btn_text_select
15322                 },
15323                 {
15324                     tag : 'button',
15325                     type : 'button',
15326                     name : 'ok',
15327                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15328                     //html : 'Done'
15329                     html: btn_text_done
15330                 },
15331                 {
15332                     tag : 'button',
15333                     type : 'button',
15334                     name : 'cancel',
15335                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15336                     //html : 'Cancel'
15337                     html: btn_text_cancel
15338                 }
15339             ]
15340         };
15341         
15342         if(this.editable){
15343             buttons.cn.unshift({
15344                 tag: 'input',
15345                 cls: 'roo-select2-search-field-input'
15346             });
15347         }
15348         
15349         var _this = this;
15350         
15351         Roo.each(buttons.cn, function(c){
15352             if (_this.size) {
15353                 c.cls += ' btn-' + _this.size;
15354             }
15355
15356             if (_this.disabled) {
15357                 c.disabled = true;
15358             }
15359         });
15360         
15361         var box = {
15362             tag: 'div',
15363             style : 'display: contents',
15364             cn: [
15365                 {
15366                     tag: 'input',
15367                     type : 'hidden',
15368                     cls: 'form-hidden-field'
15369                 },
15370                 {
15371                     tag: 'ul',
15372                     cls: 'roo-select2-choices',
15373                     cn:[
15374                         {
15375                             tag: 'li',
15376                             cls: 'roo-select2-search-field',
15377                             cn: [
15378                                 buttons
15379                             ]
15380                         }
15381                     ]
15382                 }
15383             ]
15384         };
15385         
15386         var combobox = {
15387             cls: 'roo-select2-container input-group roo-select2-container-multi',
15388             cn: [
15389                 
15390                 box
15391 //                {
15392 //                    tag: 'ul',
15393 //                    cls: 'typeahead typeahead-long dropdown-menu',
15394 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15395 //                }
15396             ]
15397         };
15398         
15399         if(this.hasFeedback && !this.allowBlank){
15400             
15401             var feedback = {
15402                 tag: 'span',
15403                 cls: 'glyphicon form-control-feedback'
15404             };
15405
15406             combobox.cn.push(feedback);
15407         }
15408         
15409         
15410         
15411         var indicator = {
15412             tag : 'i',
15413             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15414             tooltip : 'This field is required'
15415         };
15416         if (Roo.bootstrap.version == 4) {
15417             indicator = {
15418                 tag : 'i',
15419                 style : 'display:none'
15420             };
15421         }
15422         if (align ==='left' && this.fieldLabel.length) {
15423             
15424             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15425             
15426             cfg.cn = [
15427                 indicator,
15428                 {
15429                     tag: 'label',
15430                     'for' :  id,
15431                     cls : 'control-label col-form-label',
15432                     html : this.fieldLabel
15433
15434                 },
15435                 {
15436                     cls : "", 
15437                     cn: [
15438                         combobox
15439                     ]
15440                 }
15441
15442             ];
15443             
15444             var labelCfg = cfg.cn[1];
15445             var contentCfg = cfg.cn[2];
15446             
15447
15448             if(this.indicatorpos == 'right'){
15449                 
15450                 cfg.cn = [
15451                     {
15452                         tag: 'label',
15453                         'for' :  id,
15454                         cls : 'control-label col-form-label',
15455                         cn : [
15456                             {
15457                                 tag : 'span',
15458                                 html : this.fieldLabel
15459                             },
15460                             indicator
15461                         ]
15462                     },
15463                     {
15464                         cls : "",
15465                         cn: [
15466                             combobox
15467                         ]
15468                     }
15469
15470                 ];
15471                 
15472                 
15473                 
15474                 labelCfg = cfg.cn[0];
15475                 contentCfg = cfg.cn[1];
15476             
15477             }
15478             
15479             if(this.labelWidth > 12){
15480                 labelCfg.style = "width: " + this.labelWidth + 'px';
15481             }
15482             if(this.width * 1 > 0){
15483                 contentCfg.style = "width: " + this.width + 'px';
15484             }
15485             if(this.labelWidth < 13 && this.labelmd == 0){
15486                 this.labelmd = this.labelWidth;
15487             }
15488             
15489             if(this.labellg > 0){
15490                 labelCfg.cls += ' col-lg-' + this.labellg;
15491                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15492             }
15493             
15494             if(this.labelmd > 0){
15495                 labelCfg.cls += ' col-md-' + this.labelmd;
15496                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15497             }
15498             
15499             if(this.labelsm > 0){
15500                 labelCfg.cls += ' col-sm-' + this.labelsm;
15501                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15502             }
15503             
15504             if(this.labelxs > 0){
15505                 labelCfg.cls += ' col-xs-' + this.labelxs;
15506                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15507             }
15508                 
15509                 
15510         } else if ( this.fieldLabel.length) {
15511 //                Roo.log(" label");
15512                  cfg.cn = [
15513                    indicator,
15514                     {
15515                         tag: 'label',
15516                         //cls : 'input-group-addon',
15517                         html : this.fieldLabel
15518                     },
15519                     combobox
15520                 ];
15521                 
15522                 if(this.indicatorpos == 'right'){
15523                     cfg.cn = [
15524                         {
15525                             tag: 'label',
15526                             //cls : 'input-group-addon',
15527                             html : this.fieldLabel
15528                         },
15529                         indicator,
15530                         combobox
15531                     ];
15532                     
15533                 }
15534
15535         } else {
15536             
15537 //                Roo.log(" no label && no align");
15538                 cfg = combobox
15539                      
15540                 
15541         }
15542          
15543         var settings=this;
15544         ['xs','sm','md','lg'].map(function(size){
15545             if (settings[size]) {
15546                 cfg.cls += ' col-' + size + '-' + settings[size];
15547             }
15548         });
15549         
15550         return cfg;
15551         
15552     },
15553     
15554     _initEventsCalled : false,
15555     
15556     // private
15557     initEvents: function()
15558     {   
15559         if (this._initEventsCalled) { // as we call render... prevent looping...
15560             return;
15561         }
15562         this._initEventsCalled = true;
15563         
15564         if (!this.store) {
15565             throw "can not find store for combo";
15566         }
15567         
15568         this.indicator = this.indicatorEl();
15569         
15570         this.store = Roo.factory(this.store, Roo.data);
15571         this.store.parent = this;
15572         
15573         // if we are building from html. then this element is so complex, that we can not really
15574         // use the rendered HTML.
15575         // so we have to trash and replace the previous code.
15576         if (Roo.XComponent.build_from_html) {
15577             // remove this element....
15578             var e = this.el.dom, k=0;
15579             while (e ) { e = e.previousSibling;  ++k;}
15580
15581             this.el.remove();
15582             
15583             this.el=false;
15584             this.rendered = false;
15585             
15586             this.render(this.parent().getChildContainer(true), k);
15587         }
15588         
15589         if(Roo.isIOS && this.useNativeIOS){
15590             this.initIOSView();
15591             return;
15592         }
15593         
15594         /*
15595          * Touch Devices
15596          */
15597         
15598         if(Roo.isTouch && this.mobileTouchView){
15599             this.initTouchView();
15600             return;
15601         }
15602         
15603         if(this.tickable){
15604             this.initTickableEvents();
15605             return;
15606         }
15607         
15608         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15609         
15610         if(this.hiddenName){
15611             
15612             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15613             
15614             this.hiddenField.dom.value =
15615                 this.hiddenValue !== undefined ? this.hiddenValue :
15616                 this.value !== undefined ? this.value : '';
15617
15618             // prevent input submission
15619             this.el.dom.removeAttribute('name');
15620             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15621              
15622              
15623         }
15624         //if(Roo.isGecko){
15625         //    this.el.dom.setAttribute('autocomplete', 'off');
15626         //}
15627         
15628         var cls = 'x-combo-list';
15629         
15630         //this.list = new Roo.Layer({
15631         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15632         //});
15633         
15634         var _this = this;
15635         
15636         (function(){
15637             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15638             _this.list.setWidth(lw);
15639         }).defer(100);
15640         
15641         this.list.on('mouseover', this.onViewOver, this);
15642         this.list.on('mousemove', this.onViewMove, this);
15643         this.list.on('scroll', this.onViewScroll, this);
15644         
15645         /*
15646         this.list.swallowEvent('mousewheel');
15647         this.assetHeight = 0;
15648
15649         if(this.title){
15650             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15651             this.assetHeight += this.header.getHeight();
15652         }
15653
15654         this.innerList = this.list.createChild({cls:cls+'-inner'});
15655         this.innerList.on('mouseover', this.onViewOver, this);
15656         this.innerList.on('mousemove', this.onViewMove, this);
15657         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15658         
15659         if(this.allowBlank && !this.pageSize && !this.disableClear){
15660             this.footer = this.list.createChild({cls:cls+'-ft'});
15661             this.pageTb = new Roo.Toolbar(this.footer);
15662            
15663         }
15664         if(this.pageSize){
15665             this.footer = this.list.createChild({cls:cls+'-ft'});
15666             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15667                     {pageSize: this.pageSize});
15668             
15669         }
15670         
15671         if (this.pageTb && this.allowBlank && !this.disableClear) {
15672             var _this = this;
15673             this.pageTb.add(new Roo.Toolbar.Fill(), {
15674                 cls: 'x-btn-icon x-btn-clear',
15675                 text: '&#160;',
15676                 handler: function()
15677                 {
15678                     _this.collapse();
15679                     _this.clearValue();
15680                     _this.onSelect(false, -1);
15681                 }
15682             });
15683         }
15684         if (this.footer) {
15685             this.assetHeight += this.footer.getHeight();
15686         }
15687         */
15688             
15689         if(!this.tpl){
15690             this.tpl = Roo.bootstrap.version == 4 ?
15691                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15692                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15693         }
15694
15695         this.view = new Roo.View(this.list, this.tpl, {
15696             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15697         });
15698         //this.view.wrapEl.setDisplayed(false);
15699         this.view.on('click', this.onViewClick, this);
15700         
15701         
15702         this.store.on('beforeload', this.onBeforeLoad, this);
15703         this.store.on('load', this.onLoad, this);
15704         this.store.on('loadexception', this.onLoadException, this);
15705         /*
15706         if(this.resizable){
15707             this.resizer = new Roo.Resizable(this.list,  {
15708                pinned:true, handles:'se'
15709             });
15710             this.resizer.on('resize', function(r, w, h){
15711                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15712                 this.listWidth = w;
15713                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15714                 this.restrictHeight();
15715             }, this);
15716             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15717         }
15718         */
15719         if(!this.editable){
15720             this.editable = true;
15721             this.setEditable(false);
15722         }
15723         
15724         /*
15725         
15726         if (typeof(this.events.add.listeners) != 'undefined') {
15727             
15728             this.addicon = this.wrap.createChild(
15729                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
15730        
15731             this.addicon.on('click', function(e) {
15732                 this.fireEvent('add', this);
15733             }, this);
15734         }
15735         if (typeof(this.events.edit.listeners) != 'undefined') {
15736             
15737             this.editicon = this.wrap.createChild(
15738                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
15739             if (this.addicon) {
15740                 this.editicon.setStyle('margin-left', '40px');
15741             }
15742             this.editicon.on('click', function(e) {
15743                 
15744                 // we fire even  if inothing is selected..
15745                 this.fireEvent('edit', this, this.lastData );
15746                 
15747             }, this);
15748         }
15749         */
15750         
15751         this.keyNav = new Roo.KeyNav(this.inputEl(), {
15752             "up" : function(e){
15753                 this.inKeyMode = true;
15754                 this.selectPrev();
15755             },
15756
15757             "down" : function(e){
15758                 if(!this.isExpanded()){
15759                     this.onTriggerClick();
15760                 }else{
15761                     this.inKeyMode = true;
15762                     this.selectNext();
15763                 }
15764             },
15765
15766             "enter" : function(e){
15767 //                this.onViewClick();
15768                 //return true;
15769                 this.collapse();
15770                 
15771                 if(this.fireEvent("specialkey", this, e)){
15772                     this.onViewClick(false);
15773                 }
15774                 
15775                 return true;
15776             },
15777
15778             "esc" : function(e){
15779                 this.collapse();
15780             },
15781
15782             "tab" : function(e){
15783                 this.collapse();
15784                 
15785                 if(this.fireEvent("specialkey", this, e)){
15786                     this.onViewClick(false);
15787                 }
15788                 
15789                 return true;
15790             },
15791
15792             scope : this,
15793
15794             doRelay : function(foo, bar, hname){
15795                 if(hname == 'down' || this.scope.isExpanded()){
15796                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15797                 }
15798                 return true;
15799             },
15800
15801             forceKeyDown: true
15802         });
15803         
15804         
15805         this.queryDelay = Math.max(this.queryDelay || 10,
15806                 this.mode == 'local' ? 10 : 250);
15807         
15808         
15809         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15810         
15811         if(this.typeAhead){
15812             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15813         }
15814         if(this.editable !== false){
15815             this.inputEl().on("keyup", this.onKeyUp, this);
15816         }
15817         if(this.forceSelection){
15818             this.inputEl().on('blur', this.doForce, this);
15819         }
15820         
15821         if(this.multiple){
15822             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15823             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15824         }
15825     },
15826     
15827     initTickableEvents: function()
15828     {   
15829         this.createList();
15830         
15831         if(this.hiddenName){
15832             
15833             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15834             
15835             this.hiddenField.dom.value =
15836                 this.hiddenValue !== undefined ? this.hiddenValue :
15837                 this.value !== undefined ? this.value : '';
15838
15839             // prevent input submission
15840             this.el.dom.removeAttribute('name');
15841             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15842              
15843              
15844         }
15845         
15846 //        this.list = this.el.select('ul.dropdown-menu',true).first();
15847         
15848         this.choices = this.el.select('ul.roo-select2-choices', true).first();
15849         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15850         if(this.triggerList){
15851             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15852         }
15853          
15854         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15855         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15856         
15857         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15858         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15859         
15860         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15861         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15862         
15863         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15864         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15865         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15866         
15867         this.okBtn.hide();
15868         this.cancelBtn.hide();
15869         
15870         var _this = this;
15871         
15872         (function(){
15873             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15874             _this.list.setWidth(lw);
15875         }).defer(100);
15876         
15877         this.list.on('mouseover', this.onViewOver, this);
15878         this.list.on('mousemove', this.onViewMove, this);
15879         
15880         this.list.on('scroll', this.onViewScroll, this);
15881         
15882         if(!this.tpl){
15883             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
15884                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15885         }
15886
15887         this.view = new Roo.View(this.list, this.tpl, {
15888             singleSelect:true,
15889             tickable:true,
15890             parent:this,
15891             store: this.store,
15892             selectedClass: this.selectedClass
15893         });
15894         
15895         //this.view.wrapEl.setDisplayed(false);
15896         this.view.on('click', this.onViewClick, this);
15897         
15898         
15899         
15900         this.store.on('beforeload', this.onBeforeLoad, this);
15901         this.store.on('load', this.onLoad, this);
15902         this.store.on('loadexception', this.onLoadException, this);
15903         
15904         if(this.editable){
15905             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15906                 "up" : function(e){
15907                     this.inKeyMode = true;
15908                     this.selectPrev();
15909                 },
15910
15911                 "down" : function(e){
15912                     this.inKeyMode = true;
15913                     this.selectNext();
15914                 },
15915
15916                 "enter" : function(e){
15917                     if(this.fireEvent("specialkey", this, e)){
15918                         this.onViewClick(false);
15919                     }
15920                     
15921                     return true;
15922                 },
15923
15924                 "esc" : function(e){
15925                     this.onTickableFooterButtonClick(e, false, false);
15926                 },
15927
15928                 "tab" : function(e){
15929                     this.fireEvent("specialkey", this, e);
15930                     
15931                     this.onTickableFooterButtonClick(e, false, false);
15932                     
15933                     return true;
15934                 },
15935
15936                 scope : this,
15937
15938                 doRelay : function(e, fn, key){
15939                     if(this.scope.isExpanded()){
15940                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15941                     }
15942                     return true;
15943                 },
15944
15945                 forceKeyDown: true
15946             });
15947         }
15948         
15949         this.queryDelay = Math.max(this.queryDelay || 10,
15950                 this.mode == 'local' ? 10 : 250);
15951         
15952         
15953         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15954         
15955         if(this.typeAhead){
15956             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15957         }
15958         
15959         if(this.editable !== false){
15960             this.tickableInputEl().on("keyup", this.onKeyUp, this);
15961         }
15962         
15963         this.indicator = this.indicatorEl();
15964         
15965         if(this.indicator){
15966             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15967             this.indicator.hide();
15968         }
15969         
15970     },
15971
15972     onDestroy : function(){
15973         if(this.view){
15974             this.view.setStore(null);
15975             this.view.el.removeAllListeners();
15976             this.view.el.remove();
15977             this.view.purgeListeners();
15978         }
15979         if(this.list){
15980             this.list.dom.innerHTML  = '';
15981         }
15982         
15983         if(this.store){
15984             this.store.un('beforeload', this.onBeforeLoad, this);
15985             this.store.un('load', this.onLoad, this);
15986             this.store.un('loadexception', this.onLoadException, this);
15987         }
15988         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15989     },
15990
15991     // private
15992     fireKey : function(e){
15993         if(e.isNavKeyPress() && !this.list.isVisible()){
15994             this.fireEvent("specialkey", this, e);
15995         }
15996     },
15997
15998     // private
15999     onResize: function(w, h)
16000     {
16001         
16002         
16003 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16004 //        
16005 //        if(typeof w != 'number'){
16006 //            // we do not handle it!?!?
16007 //            return;
16008 //        }
16009 //        var tw = this.trigger.getWidth();
16010 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
16011 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
16012 //        var x = w - tw;
16013 //        this.inputEl().setWidth( this.adjustWidth('input', x));
16014 //            
16015 //        //this.trigger.setStyle('left', x+'px');
16016 //        
16017 //        if(this.list && this.listWidth === undefined){
16018 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16019 //            this.list.setWidth(lw);
16020 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16021 //        }
16022         
16023     
16024         
16025     },
16026
16027     /**
16028      * Allow or prevent the user from directly editing the field text.  If false is passed,
16029      * the user will only be able to select from the items defined in the dropdown list.  This method
16030      * is the runtime equivalent of setting the 'editable' config option at config time.
16031      * @param {Boolean} value True to allow the user to directly edit the field text
16032      */
16033     setEditable : function(value){
16034         if(value == this.editable){
16035             return;
16036         }
16037         this.editable = value;
16038         if(!value){
16039             this.inputEl().dom.setAttribute('readOnly', true);
16040             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16041             this.inputEl().addClass('x-combo-noedit');
16042         }else{
16043             this.inputEl().dom.setAttribute('readOnly', false);
16044             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16045             this.inputEl().removeClass('x-combo-noedit');
16046         }
16047     },
16048
16049     // private
16050     
16051     onBeforeLoad : function(combo,opts){
16052         if(!this.hasFocus){
16053             return;
16054         }
16055          if (!opts.add) {
16056             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16057          }
16058         this.restrictHeight();
16059         this.selectedIndex = -1;
16060     },
16061
16062     // private
16063     onLoad : function(){
16064         
16065         this.hasQuery = false;
16066         
16067         if(!this.hasFocus){
16068             return;
16069         }
16070         
16071         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16072             this.loading.hide();
16073         }
16074         
16075         if(this.store.getCount() > 0){
16076             
16077             this.expand();
16078             this.restrictHeight();
16079             if(this.lastQuery == this.allQuery){
16080                 if(this.editable && !this.tickable){
16081                     this.inputEl().dom.select();
16082                 }
16083                 
16084                 if(
16085                     !this.selectByValue(this.value, true) &&
16086                     this.autoFocus && 
16087                     (
16088                         !this.store.lastOptions ||
16089                         typeof(this.store.lastOptions.add) == 'undefined' || 
16090                         this.store.lastOptions.add != true
16091                     )
16092                 ){
16093                     this.select(0, true);
16094                 }
16095             }else{
16096                 if(this.autoFocus){
16097                     this.selectNext();
16098                 }
16099                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16100                     this.taTask.delay(this.typeAheadDelay);
16101                 }
16102             }
16103         }else{
16104             this.onEmptyResults();
16105         }
16106         
16107         //this.el.focus();
16108     },
16109     // private
16110     onLoadException : function()
16111     {
16112         this.hasQuery = false;
16113         
16114         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16115             this.loading.hide();
16116         }
16117         
16118         if(this.tickable && this.editable){
16119             return;
16120         }
16121         
16122         this.collapse();
16123         // only causes errors at present
16124         //Roo.log(this.store.reader.jsonData);
16125         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16126             // fixme
16127             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16128         //}
16129         
16130         
16131     },
16132     // private
16133     onTypeAhead : function(){
16134         if(this.store.getCount() > 0){
16135             var r = this.store.getAt(0);
16136             var newValue = r.data[this.displayField];
16137             var len = newValue.length;
16138             var selStart = this.getRawValue().length;
16139             
16140             if(selStart != len){
16141                 this.setRawValue(newValue);
16142                 this.selectText(selStart, newValue.length);
16143             }
16144         }
16145     },
16146
16147     // private
16148     onSelect : function(record, index){
16149         
16150         if(this.fireEvent('beforeselect', this, record, index) !== false){
16151         
16152             this.setFromData(index > -1 ? record.data : false);
16153             
16154             this.collapse();
16155             this.fireEvent('select', this, record, index);
16156         }
16157     },
16158
16159     /**
16160      * Returns the currently selected field value or empty string if no value is set.
16161      * @return {String} value The selected value
16162      */
16163     getValue : function()
16164     {
16165         if(Roo.isIOS && this.useNativeIOS){
16166             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16167         }
16168         
16169         if(this.multiple){
16170             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16171         }
16172         
16173         if(this.valueField){
16174             return typeof this.value != 'undefined' ? this.value : '';
16175         }else{
16176             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16177         }
16178     },
16179     
16180     getRawValue : function()
16181     {
16182         if(Roo.isIOS && this.useNativeIOS){
16183             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16184         }
16185         
16186         var v = this.inputEl().getValue();
16187         
16188         return v;
16189     },
16190
16191     /**
16192      * Clears any text/value currently set in the field
16193      */
16194     clearValue : function(){
16195         
16196         if(this.hiddenField){
16197             this.hiddenField.dom.value = '';
16198         }
16199         this.value = '';
16200         this.setRawValue('');
16201         this.lastSelectionText = '';
16202         this.lastData = false;
16203         
16204         var close = this.closeTriggerEl();
16205         
16206         if(close){
16207             close.hide();
16208         }
16209         
16210         this.validate();
16211         
16212     },
16213
16214     /**
16215      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16216      * will be displayed in the field.  If the value does not match the data value of an existing item,
16217      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16218      * Otherwise the field will be blank (although the value will still be set).
16219      * @param {String} value The value to match
16220      */
16221     setValue : function(v)
16222     {
16223         if(Roo.isIOS && this.useNativeIOS){
16224             this.setIOSValue(v);
16225             return;
16226         }
16227         
16228         if(this.multiple){
16229             this.syncValue();
16230             return;
16231         }
16232         
16233         var text = v;
16234         if(this.valueField){
16235             var r = this.findRecord(this.valueField, v);
16236             if(r){
16237                 text = r.data[this.displayField];
16238             }else if(this.valueNotFoundText !== undefined){
16239                 text = this.valueNotFoundText;
16240             }
16241         }
16242         this.lastSelectionText = text;
16243         if(this.hiddenField){
16244             this.hiddenField.dom.value = v;
16245         }
16246         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16247         this.value = v;
16248         
16249         var close = this.closeTriggerEl();
16250         
16251         if(close){
16252             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16253         }
16254         
16255         this.validate();
16256     },
16257     /**
16258      * @property {Object} the last set data for the element
16259      */
16260     
16261     lastData : false,
16262     /**
16263      * Sets the value of the field based on a object which is related to the record format for the store.
16264      * @param {Object} value the value to set as. or false on reset?
16265      */
16266     setFromData : function(o){
16267         
16268         if(this.multiple){
16269             this.addItem(o);
16270             return;
16271         }
16272             
16273         var dv = ''; // display value
16274         var vv = ''; // value value..
16275         this.lastData = o;
16276         if (this.displayField) {
16277             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16278         } else {
16279             // this is an error condition!!!
16280             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16281         }
16282         
16283         if(this.valueField){
16284             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16285         }
16286         
16287         var close = this.closeTriggerEl();
16288         
16289         if(close){
16290             if(dv.length || vv * 1 > 0){
16291                 close.show() ;
16292                 this.blockFocus=true;
16293             } else {
16294                 close.hide();
16295             }             
16296         }
16297         
16298         if(this.hiddenField){
16299             this.hiddenField.dom.value = vv;
16300             
16301             this.lastSelectionText = dv;
16302             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16303             this.value = vv;
16304             return;
16305         }
16306         // no hidden field.. - we store the value in 'value', but still display
16307         // display field!!!!
16308         this.lastSelectionText = dv;
16309         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16310         this.value = vv;
16311         
16312         
16313         
16314     },
16315     // private
16316     reset : function(){
16317         // overridden so that last data is reset..
16318         
16319         if(this.multiple){
16320             this.clearItem();
16321             return;
16322         }
16323         
16324         this.setValue(this.originalValue);
16325         //this.clearInvalid();
16326         this.lastData = false;
16327         if (this.view) {
16328             this.view.clearSelections();
16329         }
16330         
16331         this.validate();
16332     },
16333     // private
16334     findRecord : function(prop, value){
16335         var record;
16336         if(this.store.getCount() > 0){
16337             this.store.each(function(r){
16338                 if(r.data[prop] == value){
16339                     record = r;
16340                     return false;
16341                 }
16342                 return true;
16343             });
16344         }
16345         return record;
16346     },
16347     
16348     getName: function()
16349     {
16350         // returns hidden if it's set..
16351         if (!this.rendered) {return ''};
16352         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16353         
16354     },
16355     // private
16356     onViewMove : function(e, t){
16357         this.inKeyMode = false;
16358     },
16359
16360     // private
16361     onViewOver : function(e, t){
16362         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16363             return;
16364         }
16365         var item = this.view.findItemFromChild(t);
16366         
16367         if(item){
16368             var index = this.view.indexOf(item);
16369             this.select(index, false);
16370         }
16371     },
16372
16373     // private
16374     onViewClick : function(view, doFocus, el, e)
16375     {
16376         var index = this.view.getSelectedIndexes()[0];
16377         
16378         var r = this.store.getAt(index);
16379         
16380         if(this.tickable){
16381             
16382             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16383                 return;
16384             }
16385             
16386             var rm = false;
16387             var _this = this;
16388             
16389             Roo.each(this.tickItems, function(v,k){
16390                 
16391                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16392                     Roo.log(v);
16393                     _this.tickItems.splice(k, 1);
16394                     
16395                     if(typeof(e) == 'undefined' && view == false){
16396                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16397                     }
16398                     
16399                     rm = true;
16400                     return;
16401                 }
16402             });
16403             
16404             if(rm){
16405                 return;
16406             }
16407             
16408             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16409                 this.tickItems.push(r.data);
16410             }
16411             
16412             if(typeof(e) == 'undefined' && view == false){
16413                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16414             }
16415                     
16416             return;
16417         }
16418         
16419         if(r){
16420             this.onSelect(r, index);
16421         }
16422         if(doFocus !== false && !this.blockFocus){
16423             this.inputEl().focus();
16424         }
16425     },
16426
16427     // private
16428     restrictHeight : function(){
16429         //this.innerList.dom.style.height = '';
16430         //var inner = this.innerList.dom;
16431         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16432         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16433         //this.list.beginUpdate();
16434         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16435         this.list.alignTo(this.inputEl(), this.listAlign);
16436         this.list.alignTo(this.inputEl(), this.listAlign);
16437         //this.list.endUpdate();
16438     },
16439
16440     // private
16441     onEmptyResults : function(){
16442         
16443         if(this.tickable && this.editable){
16444             this.hasFocus = false;
16445             this.restrictHeight();
16446             return;
16447         }
16448         
16449         this.collapse();
16450     },
16451
16452     /**
16453      * Returns true if the dropdown list is expanded, else false.
16454      */
16455     isExpanded : function(){
16456         return this.list.isVisible();
16457     },
16458
16459     /**
16460      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16461      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16462      * @param {String} value The data value of the item to select
16463      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16464      * selected item if it is not currently in view (defaults to true)
16465      * @return {Boolean} True if the value matched an item in the list, else false
16466      */
16467     selectByValue : function(v, scrollIntoView){
16468         if(v !== undefined && v !== null){
16469             var r = this.findRecord(this.valueField || this.displayField, v);
16470             if(r){
16471                 this.select(this.store.indexOf(r), scrollIntoView);
16472                 return true;
16473             }
16474         }
16475         return false;
16476     },
16477
16478     /**
16479      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16480      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16481      * @param {Number} index The zero-based index of the list item to select
16482      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16483      * selected item if it is not currently in view (defaults to true)
16484      */
16485     select : function(index, scrollIntoView){
16486         this.selectedIndex = index;
16487         this.view.select(index);
16488         if(scrollIntoView !== false){
16489             var el = this.view.getNode(index);
16490             /*
16491              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16492              */
16493             if(el){
16494                 this.list.scrollChildIntoView(el, false);
16495             }
16496         }
16497     },
16498
16499     // private
16500     selectNext : function(){
16501         var ct = this.store.getCount();
16502         if(ct > 0){
16503             if(this.selectedIndex == -1){
16504                 this.select(0);
16505             }else if(this.selectedIndex < ct-1){
16506                 this.select(this.selectedIndex+1);
16507             }
16508         }
16509     },
16510
16511     // private
16512     selectPrev : function(){
16513         var ct = this.store.getCount();
16514         if(ct > 0){
16515             if(this.selectedIndex == -1){
16516                 this.select(0);
16517             }else if(this.selectedIndex != 0){
16518                 this.select(this.selectedIndex-1);
16519             }
16520         }
16521     },
16522
16523     // private
16524     onKeyUp : function(e){
16525         if(this.editable !== false && !e.isSpecialKey()){
16526             this.lastKey = e.getKey();
16527             this.dqTask.delay(this.queryDelay);
16528         }
16529     },
16530
16531     // private
16532     validateBlur : function(){
16533         return !this.list || !this.list.isVisible();   
16534     },
16535
16536     // private
16537     initQuery : function(){
16538         
16539         var v = this.getRawValue();
16540         
16541         if(this.tickable && this.editable){
16542             v = this.tickableInputEl().getValue();
16543         }
16544         
16545         this.doQuery(v);
16546     },
16547
16548     // private
16549     doForce : function(){
16550         if(this.inputEl().dom.value.length > 0){
16551             this.inputEl().dom.value =
16552                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16553              
16554         }
16555     },
16556
16557     /**
16558      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16559      * query allowing the query action to be canceled if needed.
16560      * @param {String} query The SQL query to execute
16561      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16562      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16563      * saved in the current store (defaults to false)
16564      */
16565     doQuery : function(q, forceAll){
16566         
16567         if(q === undefined || q === null){
16568             q = '';
16569         }
16570         var qe = {
16571             query: q,
16572             forceAll: forceAll,
16573             combo: this,
16574             cancel:false
16575         };
16576         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16577             return false;
16578         }
16579         q = qe.query;
16580         
16581         forceAll = qe.forceAll;
16582         if(forceAll === true || (q.length >= this.minChars)){
16583             
16584             this.hasQuery = true;
16585             
16586             if(this.lastQuery != q || this.alwaysQuery){
16587                 this.lastQuery = q;
16588                 if(this.mode == 'local'){
16589                     this.selectedIndex = -1;
16590                     if(forceAll){
16591                         this.store.clearFilter();
16592                     }else{
16593                         
16594                         if(this.specialFilter){
16595                             this.fireEvent('specialfilter', this);
16596                             this.onLoad();
16597                             return;
16598                         }
16599                         
16600                         this.store.filter(this.displayField, q);
16601                     }
16602                     
16603                     this.store.fireEvent("datachanged", this.store);
16604                     
16605                     this.onLoad();
16606                     
16607                     
16608                 }else{
16609                     
16610                     this.store.baseParams[this.queryParam] = q;
16611                     
16612                     var options = {params : this.getParams(q)};
16613                     
16614                     if(this.loadNext){
16615                         options.add = true;
16616                         options.params.start = this.page * this.pageSize;
16617                     }
16618                     
16619                     this.store.load(options);
16620                     
16621                     /*
16622                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16623                      *  we should expand the list on onLoad
16624                      *  so command out it
16625                      */
16626 //                    this.expand();
16627                 }
16628             }else{
16629                 this.selectedIndex = -1;
16630                 this.onLoad();   
16631             }
16632         }
16633         
16634         this.loadNext = false;
16635     },
16636     
16637     // private
16638     getParams : function(q){
16639         var p = {};
16640         //p[this.queryParam] = q;
16641         
16642         if(this.pageSize){
16643             p.start = 0;
16644             p.limit = this.pageSize;
16645         }
16646         return p;
16647     },
16648
16649     /**
16650      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16651      */
16652     collapse : function(){
16653         if(!this.isExpanded()){
16654             return;
16655         }
16656         
16657         this.list.hide();
16658         
16659         this.hasFocus = false;
16660         
16661         if(this.tickable){
16662             this.okBtn.hide();
16663             this.cancelBtn.hide();
16664             this.trigger.show();
16665             
16666             if(this.editable){
16667                 this.tickableInputEl().dom.value = '';
16668                 this.tickableInputEl().blur();
16669             }
16670             
16671         }
16672         
16673         Roo.get(document).un('mousedown', this.collapseIf, this);
16674         Roo.get(document).un('mousewheel', this.collapseIf, this);
16675         if (!this.editable) {
16676             Roo.get(document).un('keydown', this.listKeyPress, this);
16677         }
16678         this.fireEvent('collapse', this);
16679         
16680         this.validate();
16681     },
16682
16683     // private
16684     collapseIf : function(e){
16685         var in_combo  = e.within(this.el);
16686         var in_list =  e.within(this.list);
16687         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16688         
16689         if (in_combo || in_list || is_list) {
16690             //e.stopPropagation();
16691             return;
16692         }
16693         
16694         if(this.tickable){
16695             this.onTickableFooterButtonClick(e, false, false);
16696         }
16697
16698         this.collapse();
16699         
16700     },
16701
16702     /**
16703      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16704      */
16705     expand : function(){
16706        
16707         if(this.isExpanded() || !this.hasFocus){
16708             return;
16709         }
16710         
16711         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16712         this.list.setWidth(lw);
16713         
16714         Roo.log('expand');
16715         
16716         this.list.show();
16717         
16718         this.restrictHeight();
16719         
16720         if(this.tickable){
16721             
16722             this.tickItems = Roo.apply([], this.item);
16723             
16724             this.okBtn.show();
16725             this.cancelBtn.show();
16726             this.trigger.hide();
16727             
16728             if(this.editable){
16729                 this.tickableInputEl().focus();
16730             }
16731             
16732         }
16733         
16734         Roo.get(document).on('mousedown', this.collapseIf, this);
16735         Roo.get(document).on('mousewheel', this.collapseIf, this);
16736         if (!this.editable) {
16737             Roo.get(document).on('keydown', this.listKeyPress, this);
16738         }
16739         
16740         this.fireEvent('expand', this);
16741     },
16742
16743     // private
16744     // Implements the default empty TriggerField.onTriggerClick function
16745     onTriggerClick : function(e)
16746     {
16747         Roo.log('trigger click');
16748         
16749         if(this.disabled || !this.triggerList){
16750             return;
16751         }
16752         
16753         this.page = 0;
16754         this.loadNext = false;
16755         
16756         if(this.isExpanded()){
16757             this.collapse();
16758             if (!this.blockFocus) {
16759                 this.inputEl().focus();
16760             }
16761             
16762         }else {
16763             this.hasFocus = true;
16764             if(this.triggerAction == 'all') {
16765                 this.doQuery(this.allQuery, true);
16766             } else {
16767                 this.doQuery(this.getRawValue());
16768             }
16769             if (!this.blockFocus) {
16770                 this.inputEl().focus();
16771             }
16772         }
16773     },
16774     
16775     onTickableTriggerClick : function(e)
16776     {
16777         if(this.disabled){
16778             return;
16779         }
16780         
16781         this.page = 0;
16782         this.loadNext = false;
16783         this.hasFocus = true;
16784         
16785         if(this.triggerAction == 'all') {
16786             this.doQuery(this.allQuery, true);
16787         } else {
16788             this.doQuery(this.getRawValue());
16789         }
16790     },
16791     
16792     onSearchFieldClick : function(e)
16793     {
16794         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16795             this.onTickableFooterButtonClick(e, false, false);
16796             return;
16797         }
16798         
16799         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16800             return;
16801         }
16802         
16803         this.page = 0;
16804         this.loadNext = false;
16805         this.hasFocus = true;
16806         
16807         if(this.triggerAction == 'all') {
16808             this.doQuery(this.allQuery, true);
16809         } else {
16810             this.doQuery(this.getRawValue());
16811         }
16812     },
16813     
16814     listKeyPress : function(e)
16815     {
16816         //Roo.log('listkeypress');
16817         // scroll to first matching element based on key pres..
16818         if (e.isSpecialKey()) {
16819             return false;
16820         }
16821         var k = String.fromCharCode(e.getKey()).toUpperCase();
16822         //Roo.log(k);
16823         var match  = false;
16824         var csel = this.view.getSelectedNodes();
16825         var cselitem = false;
16826         if (csel.length) {
16827             var ix = this.view.indexOf(csel[0]);
16828             cselitem  = this.store.getAt(ix);
16829             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16830                 cselitem = false;
16831             }
16832             
16833         }
16834         
16835         this.store.each(function(v) { 
16836             if (cselitem) {
16837                 // start at existing selection.
16838                 if (cselitem.id == v.id) {
16839                     cselitem = false;
16840                 }
16841                 return true;
16842             }
16843                 
16844             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16845                 match = this.store.indexOf(v);
16846                 return false;
16847             }
16848             return true;
16849         }, this);
16850         
16851         if (match === false) {
16852             return true; // no more action?
16853         }
16854         // scroll to?
16855         this.view.select(match);
16856         var sn = Roo.get(this.view.getSelectedNodes()[0]);
16857         sn.scrollIntoView(sn.dom.parentNode, false);
16858     },
16859     
16860     onViewScroll : function(e, t){
16861         
16862         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){
16863             return;
16864         }
16865         
16866         this.hasQuery = true;
16867         
16868         this.loading = this.list.select('.loading', true).first();
16869         
16870         if(this.loading === null){
16871             this.list.createChild({
16872                 tag: 'div',
16873                 cls: 'loading roo-select2-more-results roo-select2-active',
16874                 html: 'Loading more results...'
16875             });
16876             
16877             this.loading = this.list.select('.loading', true).first();
16878             
16879             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16880             
16881             this.loading.hide();
16882         }
16883         
16884         this.loading.show();
16885         
16886         var _combo = this;
16887         
16888         this.page++;
16889         this.loadNext = true;
16890         
16891         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16892         
16893         return;
16894     },
16895     
16896     addItem : function(o)
16897     {   
16898         var dv = ''; // display value
16899         
16900         if (this.displayField) {
16901             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16902         } else {
16903             // this is an error condition!!!
16904             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16905         }
16906         
16907         if(!dv.length){
16908             return;
16909         }
16910         
16911         var choice = this.choices.createChild({
16912             tag: 'li',
16913             cls: 'roo-select2-search-choice',
16914             cn: [
16915                 {
16916                     tag: 'div',
16917                     html: dv
16918                 },
16919                 {
16920                     tag: 'a',
16921                     href: '#',
16922                     cls: 'roo-select2-search-choice-close fa fa-times',
16923                     tabindex: '-1'
16924                 }
16925             ]
16926             
16927         }, this.searchField);
16928         
16929         var close = choice.select('a.roo-select2-search-choice-close', true).first();
16930         
16931         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16932         
16933         this.item.push(o);
16934         
16935         this.lastData = o;
16936         
16937         this.syncValue();
16938         
16939         this.inputEl().dom.value = '';
16940         
16941         this.validate();
16942     },
16943     
16944     onRemoveItem : function(e, _self, o)
16945     {
16946         e.preventDefault();
16947         
16948         this.lastItem = Roo.apply([], this.item);
16949         
16950         var index = this.item.indexOf(o.data) * 1;
16951         
16952         if( index < 0){
16953             Roo.log('not this item?!');
16954             return;
16955         }
16956         
16957         this.item.splice(index, 1);
16958         o.item.remove();
16959         
16960         this.syncValue();
16961         
16962         this.fireEvent('remove', this, e);
16963         
16964         this.validate();
16965         
16966     },
16967     
16968     syncValue : function()
16969     {
16970         if(!this.item.length){
16971             this.clearValue();
16972             return;
16973         }
16974             
16975         var value = [];
16976         var _this = this;
16977         Roo.each(this.item, function(i){
16978             if(_this.valueField){
16979                 value.push(i[_this.valueField]);
16980                 return;
16981             }
16982
16983             value.push(i);
16984         });
16985
16986         this.value = value.join(',');
16987
16988         if(this.hiddenField){
16989             this.hiddenField.dom.value = this.value;
16990         }
16991         
16992         this.store.fireEvent("datachanged", this.store);
16993         
16994         this.validate();
16995     },
16996     
16997     clearItem : function()
16998     {
16999         if(!this.multiple){
17000             return;
17001         }
17002         
17003         this.item = [];
17004         
17005         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17006            c.remove();
17007         });
17008         
17009         this.syncValue();
17010         
17011         this.validate();
17012         
17013         if(this.tickable && !Roo.isTouch){
17014             this.view.refresh();
17015         }
17016     },
17017     
17018     inputEl: function ()
17019     {
17020         if(Roo.isIOS && this.useNativeIOS){
17021             return this.el.select('select.roo-ios-select', true).first();
17022         }
17023         
17024         if(Roo.isTouch && this.mobileTouchView){
17025             return this.el.select('input.form-control',true).first();
17026         }
17027         
17028         if(this.tickable){
17029             return this.searchField;
17030         }
17031         
17032         return this.el.select('input.form-control',true).first();
17033     },
17034     
17035     onTickableFooterButtonClick : function(e, btn, el)
17036     {
17037         e.preventDefault();
17038         
17039         this.lastItem = Roo.apply([], this.item);
17040         
17041         if(btn && btn.name == 'cancel'){
17042             this.tickItems = Roo.apply([], this.item);
17043             this.collapse();
17044             return;
17045         }
17046         
17047         this.clearItem();
17048         
17049         var _this = this;
17050         
17051         Roo.each(this.tickItems, function(o){
17052             _this.addItem(o);
17053         });
17054         
17055         this.collapse();
17056         
17057     },
17058     
17059     validate : function()
17060     {
17061         if(this.getVisibilityEl().hasClass('hidden')){
17062             return true;
17063         }
17064         
17065         var v = this.getRawValue();
17066         
17067         if(this.multiple){
17068             v = this.getValue();
17069         }
17070         
17071         if(this.disabled || this.allowBlank || v.length){
17072             this.markValid();
17073             return true;
17074         }
17075         
17076         this.markInvalid();
17077         return false;
17078     },
17079     
17080     tickableInputEl : function()
17081     {
17082         if(!this.tickable || !this.editable){
17083             return this.inputEl();
17084         }
17085         
17086         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17087     },
17088     
17089     
17090     getAutoCreateTouchView : function()
17091     {
17092         var id = Roo.id();
17093         
17094         var cfg = {
17095             cls: 'form-group' //input-group
17096         };
17097         
17098         var input =  {
17099             tag: 'input',
17100             id : id,
17101             type : this.inputType,
17102             cls : 'form-control x-combo-noedit',
17103             autocomplete: 'new-password',
17104             placeholder : this.placeholder || '',
17105             readonly : true
17106         };
17107         
17108         if (this.name) {
17109             input.name = this.name;
17110         }
17111         
17112         if (this.size) {
17113             input.cls += ' input-' + this.size;
17114         }
17115         
17116         if (this.disabled) {
17117             input.disabled = true;
17118         }
17119         
17120         var inputblock = {
17121             cls : 'roo-combobox-wrap',
17122             cn : [
17123                 input
17124             ]
17125         };
17126         
17127         if(this.before){
17128             inputblock.cls += ' input-group';
17129             
17130             inputblock.cn.unshift({
17131                 tag :'span',
17132                 cls : 'input-group-addon input-group-prepend input-group-text',
17133                 html : this.before
17134             });
17135         }
17136         
17137         if(this.removable && !this.multiple){
17138             inputblock.cls += ' roo-removable';
17139             
17140             inputblock.cn.push({
17141                 tag: 'button',
17142                 html : 'x',
17143                 cls : 'roo-combo-removable-btn close'
17144             });
17145         }
17146
17147         if(this.hasFeedback && !this.allowBlank){
17148             
17149             inputblock.cls += ' has-feedback';
17150             
17151             inputblock.cn.push({
17152                 tag: 'span',
17153                 cls: 'glyphicon form-control-feedback'
17154             });
17155             
17156         }
17157         
17158         if (this.after) {
17159             
17160             inputblock.cls += (this.before) ? '' : ' input-group';
17161             
17162             inputblock.cn.push({
17163                 tag :'span',
17164                 cls : 'input-group-addon input-group-append input-group-text',
17165                 html : this.after
17166             });
17167         }
17168
17169         
17170         var ibwrap = inputblock;
17171         
17172         if(this.multiple){
17173             ibwrap = {
17174                 tag: 'ul',
17175                 cls: 'roo-select2-choices',
17176                 cn:[
17177                     {
17178                         tag: 'li',
17179                         cls: 'roo-select2-search-field',
17180                         cn: [
17181
17182                             inputblock
17183                         ]
17184                     }
17185                 ]
17186             };
17187         
17188             
17189         }
17190         
17191         var combobox = {
17192             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17193             cn: [
17194                 {
17195                     tag: 'input',
17196                     type : 'hidden',
17197                     cls: 'form-hidden-field'
17198                 },
17199                 ibwrap
17200             ]
17201         };
17202         
17203         if(!this.multiple && this.showToggleBtn){
17204             
17205             var caret = {
17206                 cls: 'caret'
17207             };
17208             
17209             if (this.caret != false) {
17210                 caret = {
17211                      tag: 'i',
17212                      cls: 'fa fa-' + this.caret
17213                 };
17214                 
17215             }
17216             
17217             combobox.cn.push({
17218                 tag :'span',
17219                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17220                 cn : [
17221                     Roo.bootstrap.version == 3 ? caret : '',
17222                     {
17223                         tag: 'span',
17224                         cls: 'combobox-clear',
17225                         cn  : [
17226                             {
17227                                 tag : 'i',
17228                                 cls: 'icon-remove'
17229                             }
17230                         ]
17231                     }
17232                 ]
17233
17234             })
17235         }
17236         
17237         if(this.multiple){
17238             combobox.cls += ' roo-select2-container-multi';
17239         }
17240         
17241         var align = this.labelAlign || this.parentLabelAlign();
17242         
17243         if (align ==='left' && this.fieldLabel.length) {
17244
17245             cfg.cn = [
17246                 {
17247                    tag : 'i',
17248                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17249                    tooltip : 'This field is required'
17250                 },
17251                 {
17252                     tag: 'label',
17253                     cls : 'control-label col-form-label',
17254                     html : this.fieldLabel
17255
17256                 },
17257                 {
17258                     cls : 'roo-combobox-wrap ', 
17259                     cn: [
17260                         combobox
17261                     ]
17262                 }
17263             ];
17264             
17265             var labelCfg = cfg.cn[1];
17266             var contentCfg = cfg.cn[2];
17267             
17268
17269             if(this.indicatorpos == 'right'){
17270                 cfg.cn = [
17271                     {
17272                         tag: 'label',
17273                         'for' :  id,
17274                         cls : 'control-label col-form-label',
17275                         cn : [
17276                             {
17277                                 tag : 'span',
17278                                 html : this.fieldLabel
17279                             },
17280                             {
17281                                 tag : 'i',
17282                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17283                                 tooltip : 'This field is required'
17284                             }
17285                         ]
17286                     },
17287                     {
17288                         cls : "roo-combobox-wrap ",
17289                         cn: [
17290                             combobox
17291                         ]
17292                     }
17293
17294                 ];
17295                 
17296                 labelCfg = cfg.cn[0];
17297                 contentCfg = cfg.cn[1];
17298             }
17299             
17300            
17301             
17302             if(this.labelWidth > 12){
17303                 labelCfg.style = "width: " + this.labelWidth + 'px';
17304             }
17305            
17306             if(this.labelWidth < 13 && this.labelmd == 0){
17307                 this.labelmd = this.labelWidth;
17308             }
17309             
17310             if(this.labellg > 0){
17311                 labelCfg.cls += ' col-lg-' + this.labellg;
17312                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17313             }
17314             
17315             if(this.labelmd > 0){
17316                 labelCfg.cls += ' col-md-' + this.labelmd;
17317                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17318             }
17319             
17320             if(this.labelsm > 0){
17321                 labelCfg.cls += ' col-sm-' + this.labelsm;
17322                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17323             }
17324             
17325             if(this.labelxs > 0){
17326                 labelCfg.cls += ' col-xs-' + this.labelxs;
17327                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17328             }
17329                 
17330                 
17331         } else if ( this.fieldLabel.length) {
17332             cfg.cn = [
17333                 {
17334                    tag : 'i',
17335                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17336                    tooltip : 'This field is required'
17337                 },
17338                 {
17339                     tag: 'label',
17340                     cls : 'control-label',
17341                     html : this.fieldLabel
17342
17343                 },
17344                 {
17345                     cls : '', 
17346                     cn: [
17347                         combobox
17348                     ]
17349                 }
17350             ];
17351             
17352             if(this.indicatorpos == 'right'){
17353                 cfg.cn = [
17354                     {
17355                         tag: 'label',
17356                         cls : 'control-label',
17357                         html : this.fieldLabel,
17358                         cn : [
17359                             {
17360                                tag : 'i',
17361                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17362                                tooltip : 'This field is required'
17363                             }
17364                         ]
17365                     },
17366                     {
17367                         cls : '', 
17368                         cn: [
17369                             combobox
17370                         ]
17371                     }
17372                 ];
17373             }
17374         } else {
17375             cfg.cn = combobox;    
17376         }
17377         
17378         
17379         var settings = this;
17380         
17381         ['xs','sm','md','lg'].map(function(size){
17382             if (settings[size]) {
17383                 cfg.cls += ' col-' + size + '-' + settings[size];
17384             }
17385         });
17386         
17387         return cfg;
17388     },
17389     
17390     initTouchView : function()
17391     {
17392         this.renderTouchView();
17393         
17394         this.touchViewEl.on('scroll', function(){
17395             this.el.dom.scrollTop = 0;
17396         }, this);
17397         
17398         this.originalValue = this.getValue();
17399         
17400         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17401         
17402         this.inputEl().on("click", this.showTouchView, this);
17403         if (this.triggerEl) {
17404             this.triggerEl.on("click", this.showTouchView, this);
17405         }
17406         
17407         
17408         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17409         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17410         
17411         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17412         
17413         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17414         this.store.on('load', this.onTouchViewLoad, this);
17415         this.store.on('loadexception', this.onTouchViewLoadException, this);
17416         
17417         if(this.hiddenName){
17418             
17419             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17420             
17421             this.hiddenField.dom.value =
17422                 this.hiddenValue !== undefined ? this.hiddenValue :
17423                 this.value !== undefined ? this.value : '';
17424         
17425             this.el.dom.removeAttribute('name');
17426             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17427         }
17428         
17429         if(this.multiple){
17430             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17431             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17432         }
17433         
17434         if(this.removable && !this.multiple){
17435             var close = this.closeTriggerEl();
17436             if(close){
17437                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17438                 close.on('click', this.removeBtnClick, this, close);
17439             }
17440         }
17441         /*
17442          * fix the bug in Safari iOS8
17443          */
17444         this.inputEl().on("focus", function(e){
17445             document.activeElement.blur();
17446         }, this);
17447         
17448         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17449         
17450         return;
17451         
17452         
17453     },
17454     
17455     renderTouchView : function()
17456     {
17457         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17458         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17459         
17460         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17461         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17462         
17463         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17464         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17465         this.touchViewBodyEl.setStyle('overflow', 'auto');
17466         
17467         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17468         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17469         
17470         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17471         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17472         
17473     },
17474     
17475     showTouchView : function()
17476     {
17477         if(this.disabled){
17478             return;
17479         }
17480         
17481         this.touchViewHeaderEl.hide();
17482
17483         if(this.modalTitle.length){
17484             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17485             this.touchViewHeaderEl.show();
17486         }
17487
17488         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17489         this.touchViewEl.show();
17490
17491         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17492         
17493         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17494         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17495
17496         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17497
17498         if(this.modalTitle.length){
17499             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17500         }
17501         
17502         this.touchViewBodyEl.setHeight(bodyHeight);
17503
17504         if(this.animate){
17505             var _this = this;
17506             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17507         }else{
17508             this.touchViewEl.addClass(['in','show']);
17509         }
17510         
17511         if(this._touchViewMask){
17512             Roo.get(document.body).addClass("x-body-masked");
17513             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17514             this._touchViewMask.setStyle('z-index', 10000);
17515             this._touchViewMask.addClass('show');
17516         }
17517         
17518         this.doTouchViewQuery();
17519         
17520     },
17521     
17522     hideTouchView : function()
17523     {
17524         this.touchViewEl.removeClass(['in','show']);
17525
17526         if(this.animate){
17527             var _this = this;
17528             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17529         }else{
17530             this.touchViewEl.setStyle('display', 'none');
17531         }
17532         
17533         if(this._touchViewMask){
17534             this._touchViewMask.removeClass('show');
17535             Roo.get(document.body).removeClass("x-body-masked");
17536         }
17537     },
17538     
17539     setTouchViewValue : function()
17540     {
17541         if(this.multiple){
17542             this.clearItem();
17543         
17544             var _this = this;
17545
17546             Roo.each(this.tickItems, function(o){
17547                 this.addItem(o);
17548             }, this);
17549         }
17550         
17551         this.hideTouchView();
17552     },
17553     
17554     doTouchViewQuery : function()
17555     {
17556         var qe = {
17557             query: '',
17558             forceAll: true,
17559             combo: this,
17560             cancel:false
17561         };
17562         
17563         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17564             return false;
17565         }
17566         
17567         if(!this.alwaysQuery || this.mode == 'local'){
17568             this.onTouchViewLoad();
17569             return;
17570         }
17571         
17572         this.store.load();
17573     },
17574     
17575     onTouchViewBeforeLoad : function(combo,opts)
17576     {
17577         return;
17578     },
17579
17580     // private
17581     onTouchViewLoad : function()
17582     {
17583         if(this.store.getCount() < 1){
17584             this.onTouchViewEmptyResults();
17585             return;
17586         }
17587         
17588         this.clearTouchView();
17589         
17590         var rawValue = this.getRawValue();
17591         
17592         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17593         
17594         this.tickItems = [];
17595         
17596         this.store.data.each(function(d, rowIndex){
17597             var row = this.touchViewListGroup.createChild(template);
17598             
17599             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17600                 row.addClass(d.data.cls);
17601             }
17602             
17603             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17604                 var cfg = {
17605                     data : d.data,
17606                     html : d.data[this.displayField]
17607                 };
17608                 
17609                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17610                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17611                 }
17612             }
17613             row.removeClass('selected');
17614             if(!this.multiple && this.valueField &&
17615                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17616             {
17617                 // radio buttons..
17618                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17619                 row.addClass('selected');
17620             }
17621             
17622             if(this.multiple && this.valueField &&
17623                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17624             {
17625                 
17626                 // checkboxes...
17627                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17628                 this.tickItems.push(d.data);
17629             }
17630             
17631             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17632             
17633         }, this);
17634         
17635         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17636         
17637         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17638
17639         if(this.modalTitle.length){
17640             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17641         }
17642
17643         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17644         
17645         if(this.mobile_restrict_height && listHeight < bodyHeight){
17646             this.touchViewBodyEl.setHeight(listHeight);
17647         }
17648         
17649         var _this = this;
17650         
17651         if(firstChecked && listHeight > bodyHeight){
17652             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17653         }
17654         
17655     },
17656     
17657     onTouchViewLoadException : function()
17658     {
17659         this.hideTouchView();
17660     },
17661     
17662     onTouchViewEmptyResults : function()
17663     {
17664         this.clearTouchView();
17665         
17666         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17667         
17668         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17669         
17670     },
17671     
17672     clearTouchView : function()
17673     {
17674         this.touchViewListGroup.dom.innerHTML = '';
17675     },
17676     
17677     onTouchViewClick : function(e, el, o)
17678     {
17679         e.preventDefault();
17680         
17681         var row = o.row;
17682         var rowIndex = o.rowIndex;
17683         
17684         var r = this.store.getAt(rowIndex);
17685         
17686         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17687             
17688             if(!this.multiple){
17689                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17690                     c.dom.removeAttribute('checked');
17691                 }, this);
17692
17693                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17694
17695                 this.setFromData(r.data);
17696
17697                 var close = this.closeTriggerEl();
17698
17699                 if(close){
17700                     close.show();
17701                 }
17702
17703                 this.hideTouchView();
17704
17705                 this.fireEvent('select', this, r, rowIndex);
17706
17707                 return;
17708             }
17709
17710             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17711                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17712                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17713                 return;
17714             }
17715
17716             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17717             this.addItem(r.data);
17718             this.tickItems.push(r.data);
17719         }
17720     },
17721     
17722     getAutoCreateNativeIOS : function()
17723     {
17724         var cfg = {
17725             cls: 'form-group' //input-group,
17726         };
17727         
17728         var combobox =  {
17729             tag: 'select',
17730             cls : 'roo-ios-select'
17731         };
17732         
17733         if (this.name) {
17734             combobox.name = this.name;
17735         }
17736         
17737         if (this.disabled) {
17738             combobox.disabled = true;
17739         }
17740         
17741         var settings = this;
17742         
17743         ['xs','sm','md','lg'].map(function(size){
17744             if (settings[size]) {
17745                 cfg.cls += ' col-' + size + '-' + settings[size];
17746             }
17747         });
17748         
17749         cfg.cn = combobox;
17750         
17751         return cfg;
17752         
17753     },
17754     
17755     initIOSView : function()
17756     {
17757         this.store.on('load', this.onIOSViewLoad, this);
17758         
17759         return;
17760     },
17761     
17762     onIOSViewLoad : function()
17763     {
17764         if(this.store.getCount() < 1){
17765             return;
17766         }
17767         
17768         this.clearIOSView();
17769         
17770         if(this.allowBlank) {
17771             
17772             var default_text = '-- SELECT --';
17773             
17774             if(this.placeholder.length){
17775                 default_text = this.placeholder;
17776             }
17777             
17778             if(this.emptyTitle.length){
17779                 default_text += ' - ' + this.emptyTitle + ' -';
17780             }
17781             
17782             var opt = this.inputEl().createChild({
17783                 tag: 'option',
17784                 value : 0,
17785                 html : default_text
17786             });
17787             
17788             var o = {};
17789             o[this.valueField] = 0;
17790             o[this.displayField] = default_text;
17791             
17792             this.ios_options.push({
17793                 data : o,
17794                 el : opt
17795             });
17796             
17797         }
17798         
17799         this.store.data.each(function(d, rowIndex){
17800             
17801             var html = '';
17802             
17803             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17804                 html = d.data[this.displayField];
17805             }
17806             
17807             var value = '';
17808             
17809             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17810                 value = d.data[this.valueField];
17811             }
17812             
17813             var option = {
17814                 tag: 'option',
17815                 value : value,
17816                 html : html
17817             };
17818             
17819             if(this.value == d.data[this.valueField]){
17820                 option['selected'] = true;
17821             }
17822             
17823             var opt = this.inputEl().createChild(option);
17824             
17825             this.ios_options.push({
17826                 data : d.data,
17827                 el : opt
17828             });
17829             
17830         }, this);
17831         
17832         this.inputEl().on('change', function(){
17833            this.fireEvent('select', this);
17834         }, this);
17835         
17836     },
17837     
17838     clearIOSView: function()
17839     {
17840         this.inputEl().dom.innerHTML = '';
17841         
17842         this.ios_options = [];
17843     },
17844     
17845     setIOSValue: function(v)
17846     {
17847         this.value = v;
17848         
17849         if(!this.ios_options){
17850             return;
17851         }
17852         
17853         Roo.each(this.ios_options, function(opts){
17854            
17855            opts.el.dom.removeAttribute('selected');
17856            
17857            if(opts.data[this.valueField] != v){
17858                return;
17859            }
17860            
17861            opts.el.dom.setAttribute('selected', true);
17862            
17863         }, this);
17864     }
17865
17866     /** 
17867     * @cfg {Boolean} grow 
17868     * @hide 
17869     */
17870     /** 
17871     * @cfg {Number} growMin 
17872     * @hide 
17873     */
17874     /** 
17875     * @cfg {Number} growMax 
17876     * @hide 
17877     */
17878     /**
17879      * @hide
17880      * @method autoSize
17881      */
17882 });
17883
17884 Roo.apply(Roo.bootstrap.ComboBox,  {
17885     
17886     header : {
17887         tag: 'div',
17888         cls: 'modal-header',
17889         cn: [
17890             {
17891                 tag: 'h4',
17892                 cls: 'modal-title'
17893             }
17894         ]
17895     },
17896     
17897     body : {
17898         tag: 'div',
17899         cls: 'modal-body',
17900         cn: [
17901             {
17902                 tag: 'ul',
17903                 cls: 'list-group'
17904             }
17905         ]
17906     },
17907     
17908     listItemRadio : {
17909         tag: 'li',
17910         cls: 'list-group-item',
17911         cn: [
17912             {
17913                 tag: 'span',
17914                 cls: 'roo-combobox-list-group-item-value'
17915             },
17916             {
17917                 tag: 'div',
17918                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17919                 cn: [
17920                     {
17921                         tag: 'input',
17922                         type: 'radio'
17923                     },
17924                     {
17925                         tag: 'label'
17926                     }
17927                 ]
17928             }
17929         ]
17930     },
17931     
17932     listItemCheckbox : {
17933         tag: 'li',
17934         cls: 'list-group-item',
17935         cn: [
17936             {
17937                 tag: 'span',
17938                 cls: 'roo-combobox-list-group-item-value'
17939             },
17940             {
17941                 tag: 'div',
17942                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17943                 cn: [
17944                     {
17945                         tag: 'input',
17946                         type: 'checkbox'
17947                     },
17948                     {
17949                         tag: 'label'
17950                     }
17951                 ]
17952             }
17953         ]
17954     },
17955     
17956     emptyResult : {
17957         tag: 'div',
17958         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17959     },
17960     
17961     footer : {
17962         tag: 'div',
17963         cls: 'modal-footer',
17964         cn: [
17965             {
17966                 tag: 'div',
17967                 cls: 'row',
17968                 cn: [
17969                     {
17970                         tag: 'div',
17971                         cls: 'col-xs-6 text-left',
17972                         cn: {
17973                             tag: 'button',
17974                             cls: 'btn btn-danger roo-touch-view-cancel',
17975                             html: 'Cancel'
17976                         }
17977                     },
17978                     {
17979                         tag: 'div',
17980                         cls: 'col-xs-6 text-right',
17981                         cn: {
17982                             tag: 'button',
17983                             cls: 'btn btn-success roo-touch-view-ok',
17984                             html: 'OK'
17985                         }
17986                     }
17987                 ]
17988             }
17989         ]
17990         
17991     }
17992 });
17993
17994 Roo.apply(Roo.bootstrap.ComboBox,  {
17995     
17996     touchViewTemplate : {
17997         tag: 'div',
17998         cls: 'modal fade roo-combobox-touch-view',
17999         cn: [
18000             {
18001                 tag: 'div',
18002                 cls: 'modal-dialog',
18003                 style : 'position:fixed', // we have to fix position....
18004                 cn: [
18005                     {
18006                         tag: 'div',
18007                         cls: 'modal-content',
18008                         cn: [
18009                             Roo.bootstrap.ComboBox.header,
18010                             Roo.bootstrap.ComboBox.body,
18011                             Roo.bootstrap.ComboBox.footer
18012                         ]
18013                     }
18014                 ]
18015             }
18016         ]
18017     }
18018 });/*
18019  * Based on:
18020  * Ext JS Library 1.1.1
18021  * Copyright(c) 2006-2007, Ext JS, LLC.
18022  *
18023  * Originally Released Under LGPL - original licence link has changed is not relivant.
18024  *
18025  * Fork - LGPL
18026  * <script type="text/javascript">
18027  */
18028
18029 /**
18030  * @class Roo.View
18031  * @extends Roo.util.Observable
18032  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18033  * This class also supports single and multi selection modes. <br>
18034  * Create a data model bound view:
18035  <pre><code>
18036  var store = new Roo.data.Store(...);
18037
18038  var view = new Roo.View({
18039     el : "my-element",
18040     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18041  
18042     singleSelect: true,
18043     selectedClass: "ydataview-selected",
18044     store: store
18045  });
18046
18047  // listen for node click?
18048  view.on("click", function(vw, index, node, e){
18049  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18050  });
18051
18052  // load XML data
18053  dataModel.load("foobar.xml");
18054  </code></pre>
18055  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18056  * <br><br>
18057  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18058  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18059  * 
18060  * Note: old style constructor is still suported (container, template, config)
18061  * 
18062  * @constructor
18063  * Create a new View
18064  * @param {Object} config The config object
18065  * 
18066  */
18067 Roo.View = function(config, depreciated_tpl, depreciated_config){
18068     
18069     this.parent = false;
18070     
18071     if (typeof(depreciated_tpl) == 'undefined') {
18072         // new way.. - universal constructor.
18073         Roo.apply(this, config);
18074         this.el  = Roo.get(this.el);
18075     } else {
18076         // old format..
18077         this.el  = Roo.get(config);
18078         this.tpl = depreciated_tpl;
18079         Roo.apply(this, depreciated_config);
18080     }
18081     this.wrapEl  = this.el.wrap().wrap();
18082     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18083     
18084     
18085     if(typeof(this.tpl) == "string"){
18086         this.tpl = new Roo.Template(this.tpl);
18087     } else {
18088         // support xtype ctors..
18089         this.tpl = new Roo.factory(this.tpl, Roo);
18090     }
18091     
18092     
18093     this.tpl.compile();
18094     
18095     /** @private */
18096     this.addEvents({
18097         /**
18098          * @event beforeclick
18099          * Fires before a click is processed. Returns false to cancel the default action.
18100          * @param {Roo.View} this
18101          * @param {Number} index The index of the target node
18102          * @param {HTMLElement} node The target node
18103          * @param {Roo.EventObject} e The raw event object
18104          */
18105             "beforeclick" : true,
18106         /**
18107          * @event click
18108          * Fires when a template node is clicked.
18109          * @param {Roo.View} this
18110          * @param {Number} index The index of the target node
18111          * @param {HTMLElement} node The target node
18112          * @param {Roo.EventObject} e The raw event object
18113          */
18114             "click" : true,
18115         /**
18116          * @event dblclick
18117          * Fires when a template node is double clicked.
18118          * @param {Roo.View} this
18119          * @param {Number} index The index of the target node
18120          * @param {HTMLElement} node The target node
18121          * @param {Roo.EventObject} e The raw event object
18122          */
18123             "dblclick" : true,
18124         /**
18125          * @event contextmenu
18126          * Fires when a template node is right clicked.
18127          * @param {Roo.View} this
18128          * @param {Number} index The index of the target node
18129          * @param {HTMLElement} node The target node
18130          * @param {Roo.EventObject} e The raw event object
18131          */
18132             "contextmenu" : true,
18133         /**
18134          * @event selectionchange
18135          * Fires when the selected nodes change.
18136          * @param {Roo.View} this
18137          * @param {Array} selections Array of the selected nodes
18138          */
18139             "selectionchange" : true,
18140     
18141         /**
18142          * @event beforeselect
18143          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18144          * @param {Roo.View} this
18145          * @param {HTMLElement} node The node to be selected
18146          * @param {Array} selections Array of currently selected nodes
18147          */
18148             "beforeselect" : true,
18149         /**
18150          * @event preparedata
18151          * Fires on every row to render, to allow you to change the data.
18152          * @param {Roo.View} this
18153          * @param {Object} data to be rendered (change this)
18154          */
18155           "preparedata" : true
18156           
18157           
18158         });
18159
18160
18161
18162     this.el.on({
18163         "click": this.onClick,
18164         "dblclick": this.onDblClick,
18165         "contextmenu": this.onContextMenu,
18166         scope:this
18167     });
18168
18169     this.selections = [];
18170     this.nodes = [];
18171     this.cmp = new Roo.CompositeElementLite([]);
18172     if(this.store){
18173         this.store = Roo.factory(this.store, Roo.data);
18174         this.setStore(this.store, true);
18175     }
18176     
18177     if ( this.footer && this.footer.xtype) {
18178            
18179          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18180         
18181         this.footer.dataSource = this.store;
18182         this.footer.container = fctr;
18183         this.footer = Roo.factory(this.footer, Roo);
18184         fctr.insertFirst(this.el);
18185         
18186         // this is a bit insane - as the paging toolbar seems to detach the el..
18187 //        dom.parentNode.parentNode.parentNode
18188          // they get detached?
18189     }
18190     
18191     
18192     Roo.View.superclass.constructor.call(this);
18193     
18194     
18195 };
18196
18197 Roo.extend(Roo.View, Roo.util.Observable, {
18198     
18199      /**
18200      * @cfg {Roo.data.Store} store Data store to load data from.
18201      */
18202     store : false,
18203     
18204     /**
18205      * @cfg {String|Roo.Element} el The container element.
18206      */
18207     el : '',
18208     
18209     /**
18210      * @cfg {String|Roo.Template} tpl The template used by this View 
18211      */
18212     tpl : false,
18213     /**
18214      * @cfg {String} dataName the named area of the template to use as the data area
18215      *                          Works with domtemplates roo-name="name"
18216      */
18217     dataName: false,
18218     /**
18219      * @cfg {String} selectedClass The css class to add to selected nodes
18220      */
18221     selectedClass : "x-view-selected",
18222      /**
18223      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18224      */
18225     emptyText : "",
18226     
18227     /**
18228      * @cfg {String} text to display on mask (default Loading)
18229      */
18230     mask : false,
18231     /**
18232      * @cfg {Boolean} multiSelect Allow multiple selection
18233      */
18234     multiSelect : false,
18235     /**
18236      * @cfg {Boolean} singleSelect Allow single selection
18237      */
18238     singleSelect:  false,
18239     
18240     /**
18241      * @cfg {Boolean} toggleSelect - selecting 
18242      */
18243     toggleSelect : false,
18244     
18245     /**
18246      * @cfg {Boolean} tickable - selecting 
18247      */
18248     tickable : false,
18249     
18250     /**
18251      * Returns the element this view is bound to.
18252      * @return {Roo.Element}
18253      */
18254     getEl : function(){
18255         return this.wrapEl;
18256     },
18257     
18258     
18259
18260     /**
18261      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18262      */
18263     refresh : function(){
18264         //Roo.log('refresh');
18265         var t = this.tpl;
18266         
18267         // if we are using something like 'domtemplate', then
18268         // the what gets used is:
18269         // t.applySubtemplate(NAME, data, wrapping data..)
18270         // the outer template then get' applied with
18271         //     the store 'extra data'
18272         // and the body get's added to the
18273         //      roo-name="data" node?
18274         //      <span class='roo-tpl-{name}'></span> ?????
18275         
18276         
18277         
18278         this.clearSelections();
18279         this.el.update("");
18280         var html = [];
18281         var records = this.store.getRange();
18282         if(records.length < 1) {
18283             
18284             // is this valid??  = should it render a template??
18285             
18286             this.el.update(this.emptyText);
18287             return;
18288         }
18289         var el = this.el;
18290         if (this.dataName) {
18291             this.el.update(t.apply(this.store.meta)); //????
18292             el = this.el.child('.roo-tpl-' + this.dataName);
18293         }
18294         
18295         for(var i = 0, len = records.length; i < len; i++){
18296             var data = this.prepareData(records[i].data, i, records[i]);
18297             this.fireEvent("preparedata", this, data, i, records[i]);
18298             
18299             var d = Roo.apply({}, data);
18300             
18301             if(this.tickable){
18302                 Roo.apply(d, {'roo-id' : Roo.id()});
18303                 
18304                 var _this = this;
18305             
18306                 Roo.each(this.parent.item, function(item){
18307                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18308                         return;
18309                     }
18310                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18311                 });
18312             }
18313             
18314             html[html.length] = Roo.util.Format.trim(
18315                 this.dataName ?
18316                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18317                     t.apply(d)
18318             );
18319         }
18320         
18321         
18322         
18323         el.update(html.join(""));
18324         this.nodes = el.dom.childNodes;
18325         this.updateIndexes(0);
18326     },
18327     
18328
18329     /**
18330      * Function to override to reformat the data that is sent to
18331      * the template for each node.
18332      * DEPRICATED - use the preparedata event handler.
18333      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18334      * a JSON object for an UpdateManager bound view).
18335      */
18336     prepareData : function(data, index, record)
18337     {
18338         this.fireEvent("preparedata", this, data, index, record);
18339         return data;
18340     },
18341
18342     onUpdate : function(ds, record){
18343         // Roo.log('on update');   
18344         this.clearSelections();
18345         var index = this.store.indexOf(record);
18346         var n = this.nodes[index];
18347         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18348         n.parentNode.removeChild(n);
18349         this.updateIndexes(index, index);
18350     },
18351
18352     
18353     
18354 // --------- FIXME     
18355     onAdd : function(ds, records, index)
18356     {
18357         //Roo.log(['on Add', ds, records, index] );        
18358         this.clearSelections();
18359         if(this.nodes.length == 0){
18360             this.refresh();
18361             return;
18362         }
18363         var n = this.nodes[index];
18364         for(var i = 0, len = records.length; i < len; i++){
18365             var d = this.prepareData(records[i].data, i, records[i]);
18366             if(n){
18367                 this.tpl.insertBefore(n, d);
18368             }else{
18369                 
18370                 this.tpl.append(this.el, d);
18371             }
18372         }
18373         this.updateIndexes(index);
18374     },
18375
18376     onRemove : function(ds, record, index){
18377        // Roo.log('onRemove');
18378         this.clearSelections();
18379         var el = this.dataName  ?
18380             this.el.child('.roo-tpl-' + this.dataName) :
18381             this.el; 
18382         
18383         el.dom.removeChild(this.nodes[index]);
18384         this.updateIndexes(index);
18385     },
18386
18387     /**
18388      * Refresh an individual node.
18389      * @param {Number} index
18390      */
18391     refreshNode : function(index){
18392         this.onUpdate(this.store, this.store.getAt(index));
18393     },
18394
18395     updateIndexes : function(startIndex, endIndex){
18396         var ns = this.nodes;
18397         startIndex = startIndex || 0;
18398         endIndex = endIndex || ns.length - 1;
18399         for(var i = startIndex; i <= endIndex; i++){
18400             ns[i].nodeIndex = i;
18401         }
18402     },
18403
18404     /**
18405      * Changes the data store this view uses and refresh the view.
18406      * @param {Store} store
18407      */
18408     setStore : function(store, initial){
18409         if(!initial && this.store){
18410             this.store.un("datachanged", this.refresh);
18411             this.store.un("add", this.onAdd);
18412             this.store.un("remove", this.onRemove);
18413             this.store.un("update", this.onUpdate);
18414             this.store.un("clear", this.refresh);
18415             this.store.un("beforeload", this.onBeforeLoad);
18416             this.store.un("load", this.onLoad);
18417             this.store.un("loadexception", this.onLoad);
18418         }
18419         if(store){
18420           
18421             store.on("datachanged", this.refresh, this);
18422             store.on("add", this.onAdd, this);
18423             store.on("remove", this.onRemove, this);
18424             store.on("update", this.onUpdate, this);
18425             store.on("clear", this.refresh, this);
18426             store.on("beforeload", this.onBeforeLoad, this);
18427             store.on("load", this.onLoad, this);
18428             store.on("loadexception", this.onLoad, this);
18429         }
18430         
18431         if(store){
18432             this.refresh();
18433         }
18434     },
18435     /**
18436      * onbeforeLoad - masks the loading area.
18437      *
18438      */
18439     onBeforeLoad : function(store,opts)
18440     {
18441          //Roo.log('onBeforeLoad');   
18442         if (!opts.add) {
18443             this.el.update("");
18444         }
18445         this.el.mask(this.mask ? this.mask : "Loading" ); 
18446     },
18447     onLoad : function ()
18448     {
18449         this.el.unmask();
18450     },
18451     
18452
18453     /**
18454      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18455      * @param {HTMLElement} node
18456      * @return {HTMLElement} The template node
18457      */
18458     findItemFromChild : function(node){
18459         var el = this.dataName  ?
18460             this.el.child('.roo-tpl-' + this.dataName,true) :
18461             this.el.dom; 
18462         
18463         if(!node || node.parentNode == el){
18464                     return node;
18465             }
18466             var p = node.parentNode;
18467             while(p && p != el){
18468             if(p.parentNode == el){
18469                 return p;
18470             }
18471             p = p.parentNode;
18472         }
18473             return null;
18474     },
18475
18476     /** @ignore */
18477     onClick : function(e){
18478         var item = this.findItemFromChild(e.getTarget());
18479         if(item){
18480             var index = this.indexOf(item);
18481             if(this.onItemClick(item, index, e) !== false){
18482                 this.fireEvent("click", this, index, item, e);
18483             }
18484         }else{
18485             this.clearSelections();
18486         }
18487     },
18488
18489     /** @ignore */
18490     onContextMenu : function(e){
18491         var item = this.findItemFromChild(e.getTarget());
18492         if(item){
18493             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18494         }
18495     },
18496
18497     /** @ignore */
18498     onDblClick : function(e){
18499         var item = this.findItemFromChild(e.getTarget());
18500         if(item){
18501             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18502         }
18503     },
18504
18505     onItemClick : function(item, index, e)
18506     {
18507         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18508             return false;
18509         }
18510         if (this.toggleSelect) {
18511             var m = this.isSelected(item) ? 'unselect' : 'select';
18512             //Roo.log(m);
18513             var _t = this;
18514             _t[m](item, true, false);
18515             return true;
18516         }
18517         if(this.multiSelect || this.singleSelect){
18518             if(this.multiSelect && e.shiftKey && this.lastSelection){
18519                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18520             }else{
18521                 this.select(item, this.multiSelect && e.ctrlKey);
18522                 this.lastSelection = item;
18523             }
18524             
18525             if(!this.tickable){
18526                 e.preventDefault();
18527             }
18528             
18529         }
18530         return true;
18531     },
18532
18533     /**
18534      * Get the number of selected nodes.
18535      * @return {Number}
18536      */
18537     getSelectionCount : function(){
18538         return this.selections.length;
18539     },
18540
18541     /**
18542      * Get the currently selected nodes.
18543      * @return {Array} An array of HTMLElements
18544      */
18545     getSelectedNodes : function(){
18546         return this.selections;
18547     },
18548
18549     /**
18550      * Get the indexes of the selected nodes.
18551      * @return {Array}
18552      */
18553     getSelectedIndexes : function(){
18554         var indexes = [], s = this.selections;
18555         for(var i = 0, len = s.length; i < len; i++){
18556             indexes.push(s[i].nodeIndex);
18557         }
18558         return indexes;
18559     },
18560
18561     /**
18562      * Clear all selections
18563      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18564      */
18565     clearSelections : function(suppressEvent){
18566         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18567             this.cmp.elements = this.selections;
18568             this.cmp.removeClass(this.selectedClass);
18569             this.selections = [];
18570             if(!suppressEvent){
18571                 this.fireEvent("selectionchange", this, this.selections);
18572             }
18573         }
18574     },
18575
18576     /**
18577      * Returns true if the passed node is selected
18578      * @param {HTMLElement/Number} node The node or node index
18579      * @return {Boolean}
18580      */
18581     isSelected : function(node){
18582         var s = this.selections;
18583         if(s.length < 1){
18584             return false;
18585         }
18586         node = this.getNode(node);
18587         return s.indexOf(node) !== -1;
18588     },
18589
18590     /**
18591      * Selects nodes.
18592      * @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
18593      * @param {Boolean} keepExisting (optional) true to keep existing selections
18594      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18595      */
18596     select : function(nodeInfo, keepExisting, suppressEvent){
18597         if(nodeInfo instanceof Array){
18598             if(!keepExisting){
18599                 this.clearSelections(true);
18600             }
18601             for(var i = 0, len = nodeInfo.length; i < len; i++){
18602                 this.select(nodeInfo[i], true, true);
18603             }
18604             return;
18605         } 
18606         var node = this.getNode(nodeInfo);
18607         if(!node || this.isSelected(node)){
18608             return; // already selected.
18609         }
18610         if(!keepExisting){
18611             this.clearSelections(true);
18612         }
18613         
18614         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18615             Roo.fly(node).addClass(this.selectedClass);
18616             this.selections.push(node);
18617             if(!suppressEvent){
18618                 this.fireEvent("selectionchange", this, this.selections);
18619             }
18620         }
18621         
18622         
18623     },
18624       /**
18625      * Unselects nodes.
18626      * @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
18627      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18628      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18629      */
18630     unselect : function(nodeInfo, keepExisting, suppressEvent)
18631     {
18632         if(nodeInfo instanceof Array){
18633             Roo.each(this.selections, function(s) {
18634                 this.unselect(s, nodeInfo);
18635             }, this);
18636             return;
18637         }
18638         var node = this.getNode(nodeInfo);
18639         if(!node || !this.isSelected(node)){
18640             //Roo.log("not selected");
18641             return; // not selected.
18642         }
18643         // fireevent???
18644         var ns = [];
18645         Roo.each(this.selections, function(s) {
18646             if (s == node ) {
18647                 Roo.fly(node).removeClass(this.selectedClass);
18648
18649                 return;
18650             }
18651             ns.push(s);
18652         },this);
18653         
18654         this.selections= ns;
18655         this.fireEvent("selectionchange", this, this.selections);
18656     },
18657
18658     /**
18659      * Gets a template node.
18660      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18661      * @return {HTMLElement} The node or null if it wasn't found
18662      */
18663     getNode : function(nodeInfo){
18664         if(typeof nodeInfo == "string"){
18665             return document.getElementById(nodeInfo);
18666         }else if(typeof nodeInfo == "number"){
18667             return this.nodes[nodeInfo];
18668         }
18669         return nodeInfo;
18670     },
18671
18672     /**
18673      * Gets a range template nodes.
18674      * @param {Number} startIndex
18675      * @param {Number} endIndex
18676      * @return {Array} An array of nodes
18677      */
18678     getNodes : function(start, end){
18679         var ns = this.nodes;
18680         start = start || 0;
18681         end = typeof end == "undefined" ? ns.length - 1 : end;
18682         var nodes = [];
18683         if(start <= end){
18684             for(var i = start; i <= end; i++){
18685                 nodes.push(ns[i]);
18686             }
18687         } else{
18688             for(var i = start; i >= end; i--){
18689                 nodes.push(ns[i]);
18690             }
18691         }
18692         return nodes;
18693     },
18694
18695     /**
18696      * Finds the index of the passed node
18697      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18698      * @return {Number} The index of the node or -1
18699      */
18700     indexOf : function(node){
18701         node = this.getNode(node);
18702         if(typeof node.nodeIndex == "number"){
18703             return node.nodeIndex;
18704         }
18705         var ns = this.nodes;
18706         for(var i = 0, len = ns.length; i < len; i++){
18707             if(ns[i] == node){
18708                 return i;
18709             }
18710         }
18711         return -1;
18712     }
18713 });
18714 /*
18715  * - LGPL
18716  *
18717  * based on jquery fullcalendar
18718  * 
18719  */
18720
18721 Roo.bootstrap = Roo.bootstrap || {};
18722 /**
18723  * @class Roo.bootstrap.Calendar
18724  * @extends Roo.bootstrap.Component
18725  * Bootstrap Calendar class
18726  * @cfg {Boolean} loadMask (true|false) default false
18727  * @cfg {Object} header generate the user specific header of the calendar, default false
18728
18729  * @constructor
18730  * Create a new Container
18731  * @param {Object} config The config object
18732  */
18733
18734
18735
18736 Roo.bootstrap.Calendar = function(config){
18737     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18738      this.addEvents({
18739         /**
18740              * @event select
18741              * Fires when a date is selected
18742              * @param {DatePicker} this
18743              * @param {Date} date The selected date
18744              */
18745         'select': true,
18746         /**
18747              * @event monthchange
18748              * Fires when the displayed month changes 
18749              * @param {DatePicker} this
18750              * @param {Date} date The selected month
18751              */
18752         'monthchange': true,
18753         /**
18754              * @event evententer
18755              * Fires when mouse over an event
18756              * @param {Calendar} this
18757              * @param {event} Event
18758              */
18759         'evententer': true,
18760         /**
18761              * @event eventleave
18762              * Fires when the mouse leaves an
18763              * @param {Calendar} this
18764              * @param {event}
18765              */
18766         'eventleave': true,
18767         /**
18768              * @event eventclick
18769              * Fires when the mouse click an
18770              * @param {Calendar} this
18771              * @param {event}
18772              */
18773         'eventclick': true
18774         
18775     });
18776
18777 };
18778
18779 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
18780     
18781      /**
18782      * @cfg {Number} startDay
18783      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18784      */
18785     startDay : 0,
18786     
18787     loadMask : false,
18788     
18789     header : false,
18790       
18791     getAutoCreate : function(){
18792         
18793         
18794         var fc_button = function(name, corner, style, content ) {
18795             return Roo.apply({},{
18796                 tag : 'span',
18797                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
18798                          (corner.length ?
18799                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18800                             ''
18801                         ),
18802                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18803                 unselectable: 'on'
18804             });
18805         };
18806         
18807         var header = {};
18808         
18809         if(!this.header){
18810             header = {
18811                 tag : 'table',
18812                 cls : 'fc-header',
18813                 style : 'width:100%',
18814                 cn : [
18815                     {
18816                         tag: 'tr',
18817                         cn : [
18818                             {
18819                                 tag : 'td',
18820                                 cls : 'fc-header-left',
18821                                 cn : [
18822                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
18823                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
18824                                     { tag: 'span', cls: 'fc-header-space' },
18825                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
18826
18827
18828                                 ]
18829                             },
18830
18831                             {
18832                                 tag : 'td',
18833                                 cls : 'fc-header-center',
18834                                 cn : [
18835                                     {
18836                                         tag: 'span',
18837                                         cls: 'fc-header-title',
18838                                         cn : {
18839                                             tag: 'H2',
18840                                             html : 'month / year'
18841                                         }
18842                                     }
18843
18844                                 ]
18845                             },
18846                             {
18847                                 tag : 'td',
18848                                 cls : 'fc-header-right',
18849                                 cn : [
18850                               /*      fc_button('month', 'left', '', 'month' ),
18851                                     fc_button('week', '', '', 'week' ),
18852                                     fc_button('day', 'right', '', 'day' )
18853                                 */    
18854
18855                                 ]
18856                             }
18857
18858                         ]
18859                     }
18860                 ]
18861             };
18862         }
18863         
18864         header = this.header;
18865         
18866        
18867         var cal_heads = function() {
18868             var ret = [];
18869             // fixme - handle this.
18870             
18871             for (var i =0; i < Date.dayNames.length; i++) {
18872                 var d = Date.dayNames[i];
18873                 ret.push({
18874                     tag: 'th',
18875                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18876                     html : d.substring(0,3)
18877                 });
18878                 
18879             }
18880             ret[0].cls += ' fc-first';
18881             ret[6].cls += ' fc-last';
18882             return ret;
18883         };
18884         var cal_cell = function(n) {
18885             return  {
18886                 tag: 'td',
18887                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18888                 cn : [
18889                     {
18890                         cn : [
18891                             {
18892                                 cls: 'fc-day-number',
18893                                 html: 'D'
18894                             },
18895                             {
18896                                 cls: 'fc-day-content',
18897                              
18898                                 cn : [
18899                                      {
18900                                         style: 'position: relative;' // height: 17px;
18901                                     }
18902                                 ]
18903                             }
18904                             
18905                             
18906                         ]
18907                     }
18908                 ]
18909                 
18910             }
18911         };
18912         var cal_rows = function() {
18913             
18914             var ret = [];
18915             for (var r = 0; r < 6; r++) {
18916                 var row= {
18917                     tag : 'tr',
18918                     cls : 'fc-week',
18919                     cn : []
18920                 };
18921                 
18922                 for (var i =0; i < Date.dayNames.length; i++) {
18923                     var d = Date.dayNames[i];
18924                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18925
18926                 }
18927                 row.cn[0].cls+=' fc-first';
18928                 row.cn[0].cn[0].style = 'min-height:90px';
18929                 row.cn[6].cls+=' fc-last';
18930                 ret.push(row);
18931                 
18932             }
18933             ret[0].cls += ' fc-first';
18934             ret[4].cls += ' fc-prev-last';
18935             ret[5].cls += ' fc-last';
18936             return ret;
18937             
18938         };
18939         
18940         var cal_table = {
18941             tag: 'table',
18942             cls: 'fc-border-separate',
18943             style : 'width:100%',
18944             cellspacing  : 0,
18945             cn : [
18946                 { 
18947                     tag: 'thead',
18948                     cn : [
18949                         { 
18950                             tag: 'tr',
18951                             cls : 'fc-first fc-last',
18952                             cn : cal_heads()
18953                         }
18954                     ]
18955                 },
18956                 { 
18957                     tag: 'tbody',
18958                     cn : cal_rows()
18959                 }
18960                   
18961             ]
18962         };
18963          
18964          var cfg = {
18965             cls : 'fc fc-ltr',
18966             cn : [
18967                 header,
18968                 {
18969                     cls : 'fc-content',
18970                     style : "position: relative;",
18971                     cn : [
18972                         {
18973                             cls : 'fc-view fc-view-month fc-grid',
18974                             style : 'position: relative',
18975                             unselectable : 'on',
18976                             cn : [
18977                                 {
18978                                     cls : 'fc-event-container',
18979                                     style : 'position:absolute;z-index:8;top:0;left:0;'
18980                                 },
18981                                 cal_table
18982                             ]
18983                         }
18984                     ]
18985     
18986                 }
18987            ] 
18988             
18989         };
18990         
18991          
18992         
18993         return cfg;
18994     },
18995     
18996     
18997     initEvents : function()
18998     {
18999         if(!this.store){
19000             throw "can not find store for calendar";
19001         }
19002         
19003         var mark = {
19004             tag: "div",
19005             cls:"x-dlg-mask",
19006             style: "text-align:center",
19007             cn: [
19008                 {
19009                     tag: "div",
19010                     style: "background-color:white;width:50%;margin:250 auto",
19011                     cn: [
19012                         {
19013                             tag: "img",
19014                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
19015                         },
19016                         {
19017                             tag: "span",
19018                             html: "Loading"
19019                         }
19020                         
19021                     ]
19022                 }
19023             ]
19024         };
19025         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19026         
19027         var size = this.el.select('.fc-content', true).first().getSize();
19028         this.maskEl.setSize(size.width, size.height);
19029         this.maskEl.enableDisplayMode("block");
19030         if(!this.loadMask){
19031             this.maskEl.hide();
19032         }
19033         
19034         this.store = Roo.factory(this.store, Roo.data);
19035         this.store.on('load', this.onLoad, this);
19036         this.store.on('beforeload', this.onBeforeLoad, this);
19037         
19038         this.resize();
19039         
19040         this.cells = this.el.select('.fc-day',true);
19041         //Roo.log(this.cells);
19042         this.textNodes = this.el.query('.fc-day-number');
19043         this.cells.addClassOnOver('fc-state-hover');
19044         
19045         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19046         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19047         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19048         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19049         
19050         this.on('monthchange', this.onMonthChange, this);
19051         
19052         this.update(new Date().clearTime());
19053     },
19054     
19055     resize : function() {
19056         var sz  = this.el.getSize();
19057         
19058         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19059         this.el.select('.fc-day-content div',true).setHeight(34);
19060     },
19061     
19062     
19063     // private
19064     showPrevMonth : function(e){
19065         this.update(this.activeDate.add("mo", -1));
19066     },
19067     showToday : function(e){
19068         this.update(new Date().clearTime());
19069     },
19070     // private
19071     showNextMonth : function(e){
19072         this.update(this.activeDate.add("mo", 1));
19073     },
19074
19075     // private
19076     showPrevYear : function(){
19077         this.update(this.activeDate.add("y", -1));
19078     },
19079
19080     // private
19081     showNextYear : function(){
19082         this.update(this.activeDate.add("y", 1));
19083     },
19084
19085     
19086    // private
19087     update : function(date)
19088     {
19089         var vd = this.activeDate;
19090         this.activeDate = date;
19091 //        if(vd && this.el){
19092 //            var t = date.getTime();
19093 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19094 //                Roo.log('using add remove');
19095 //                
19096 //                this.fireEvent('monthchange', this, date);
19097 //                
19098 //                this.cells.removeClass("fc-state-highlight");
19099 //                this.cells.each(function(c){
19100 //                   if(c.dateValue == t){
19101 //                       c.addClass("fc-state-highlight");
19102 //                       setTimeout(function(){
19103 //                            try{c.dom.firstChild.focus();}catch(e){}
19104 //                       }, 50);
19105 //                       return false;
19106 //                   }
19107 //                   return true;
19108 //                });
19109 //                return;
19110 //            }
19111 //        }
19112         
19113         var days = date.getDaysInMonth();
19114         
19115         var firstOfMonth = date.getFirstDateOfMonth();
19116         var startingPos = firstOfMonth.getDay()-this.startDay;
19117         
19118         if(startingPos < this.startDay){
19119             startingPos += 7;
19120         }
19121         
19122         var pm = date.add(Date.MONTH, -1);
19123         var prevStart = pm.getDaysInMonth()-startingPos;
19124 //        
19125         this.cells = this.el.select('.fc-day',true);
19126         this.textNodes = this.el.query('.fc-day-number');
19127         this.cells.addClassOnOver('fc-state-hover');
19128         
19129         var cells = this.cells.elements;
19130         var textEls = this.textNodes;
19131         
19132         Roo.each(cells, function(cell){
19133             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19134         });
19135         
19136         days += startingPos;
19137
19138         // convert everything to numbers so it's fast
19139         var day = 86400000;
19140         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19141         //Roo.log(d);
19142         //Roo.log(pm);
19143         //Roo.log(prevStart);
19144         
19145         var today = new Date().clearTime().getTime();
19146         var sel = date.clearTime().getTime();
19147         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19148         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19149         var ddMatch = this.disabledDatesRE;
19150         var ddText = this.disabledDatesText;
19151         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19152         var ddaysText = this.disabledDaysText;
19153         var format = this.format;
19154         
19155         var setCellClass = function(cal, cell){
19156             cell.row = 0;
19157             cell.events = [];
19158             cell.more = [];
19159             //Roo.log('set Cell Class');
19160             cell.title = "";
19161             var t = d.getTime();
19162             
19163             //Roo.log(d);
19164             
19165             cell.dateValue = t;
19166             if(t == today){
19167                 cell.className += " fc-today";
19168                 cell.className += " fc-state-highlight";
19169                 cell.title = cal.todayText;
19170             }
19171             if(t == sel){
19172                 // disable highlight in other month..
19173                 //cell.className += " fc-state-highlight";
19174                 
19175             }
19176             // disabling
19177             if(t < min) {
19178                 cell.className = " fc-state-disabled";
19179                 cell.title = cal.minText;
19180                 return;
19181             }
19182             if(t > max) {
19183                 cell.className = " fc-state-disabled";
19184                 cell.title = cal.maxText;
19185                 return;
19186             }
19187             if(ddays){
19188                 if(ddays.indexOf(d.getDay()) != -1){
19189                     cell.title = ddaysText;
19190                     cell.className = " fc-state-disabled";
19191                 }
19192             }
19193             if(ddMatch && format){
19194                 var fvalue = d.dateFormat(format);
19195                 if(ddMatch.test(fvalue)){
19196                     cell.title = ddText.replace("%0", fvalue);
19197                     cell.className = " fc-state-disabled";
19198                 }
19199             }
19200             
19201             if (!cell.initialClassName) {
19202                 cell.initialClassName = cell.dom.className;
19203             }
19204             
19205             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19206         };
19207
19208         var i = 0;
19209         
19210         for(; i < startingPos; i++) {
19211             textEls[i].innerHTML = (++prevStart);
19212             d.setDate(d.getDate()+1);
19213             
19214             cells[i].className = "fc-past fc-other-month";
19215             setCellClass(this, cells[i]);
19216         }
19217         
19218         var intDay = 0;
19219         
19220         for(; i < days; i++){
19221             intDay = i - startingPos + 1;
19222             textEls[i].innerHTML = (intDay);
19223             d.setDate(d.getDate()+1);
19224             
19225             cells[i].className = ''; // "x-date-active";
19226             setCellClass(this, cells[i]);
19227         }
19228         var extraDays = 0;
19229         
19230         for(; i < 42; i++) {
19231             textEls[i].innerHTML = (++extraDays);
19232             d.setDate(d.getDate()+1);
19233             
19234             cells[i].className = "fc-future fc-other-month";
19235             setCellClass(this, cells[i]);
19236         }
19237         
19238         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19239         
19240         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19241         
19242         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19243         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19244         
19245         if(totalRows != 6){
19246             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19247             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19248         }
19249         
19250         this.fireEvent('monthchange', this, date);
19251         
19252         
19253         /*
19254         if(!this.internalRender){
19255             var main = this.el.dom.firstChild;
19256             var w = main.offsetWidth;
19257             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19258             Roo.fly(main).setWidth(w);
19259             this.internalRender = true;
19260             // opera does not respect the auto grow header center column
19261             // then, after it gets a width opera refuses to recalculate
19262             // without a second pass
19263             if(Roo.isOpera && !this.secondPass){
19264                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19265                 this.secondPass = true;
19266                 this.update.defer(10, this, [date]);
19267             }
19268         }
19269         */
19270         
19271     },
19272     
19273     findCell : function(dt) {
19274         dt = dt.clearTime().getTime();
19275         var ret = false;
19276         this.cells.each(function(c){
19277             //Roo.log("check " +c.dateValue + '?=' + dt);
19278             if(c.dateValue == dt){
19279                 ret = c;
19280                 return false;
19281             }
19282             return true;
19283         });
19284         
19285         return ret;
19286     },
19287     
19288     findCells : function(ev) {
19289         var s = ev.start.clone().clearTime().getTime();
19290        // Roo.log(s);
19291         var e= ev.end.clone().clearTime().getTime();
19292        // Roo.log(e);
19293         var ret = [];
19294         this.cells.each(function(c){
19295              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19296             
19297             if(c.dateValue > e){
19298                 return ;
19299             }
19300             if(c.dateValue < s){
19301                 return ;
19302             }
19303             ret.push(c);
19304         });
19305         
19306         return ret;    
19307     },
19308     
19309 //    findBestRow: function(cells)
19310 //    {
19311 //        var ret = 0;
19312 //        
19313 //        for (var i =0 ; i < cells.length;i++) {
19314 //            ret  = Math.max(cells[i].rows || 0,ret);
19315 //        }
19316 //        return ret;
19317 //        
19318 //    },
19319     
19320     
19321     addItem : function(ev)
19322     {
19323         // look for vertical location slot in
19324         var cells = this.findCells(ev);
19325         
19326 //        ev.row = this.findBestRow(cells);
19327         
19328         // work out the location.
19329         
19330         var crow = false;
19331         var rows = [];
19332         for(var i =0; i < cells.length; i++) {
19333             
19334             cells[i].row = cells[0].row;
19335             
19336             if(i == 0){
19337                 cells[i].row = cells[i].row + 1;
19338             }
19339             
19340             if (!crow) {
19341                 crow = {
19342                     start : cells[i],
19343                     end :  cells[i]
19344                 };
19345                 continue;
19346             }
19347             if (crow.start.getY() == cells[i].getY()) {
19348                 // on same row.
19349                 crow.end = cells[i];
19350                 continue;
19351             }
19352             // different row.
19353             rows.push(crow);
19354             crow = {
19355                 start: cells[i],
19356                 end : cells[i]
19357             };
19358             
19359         }
19360         
19361         rows.push(crow);
19362         ev.els = [];
19363         ev.rows = rows;
19364         ev.cells = cells;
19365         
19366         cells[0].events.push(ev);
19367         
19368         this.calevents.push(ev);
19369     },
19370     
19371     clearEvents: function() {
19372         
19373         if(!this.calevents){
19374             return;
19375         }
19376         
19377         Roo.each(this.cells.elements, function(c){
19378             c.row = 0;
19379             c.events = [];
19380             c.more = [];
19381         });
19382         
19383         Roo.each(this.calevents, function(e) {
19384             Roo.each(e.els, function(el) {
19385                 el.un('mouseenter' ,this.onEventEnter, this);
19386                 el.un('mouseleave' ,this.onEventLeave, this);
19387                 el.remove();
19388             },this);
19389         },this);
19390         
19391         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19392             e.remove();
19393         });
19394         
19395     },
19396     
19397     renderEvents: function()
19398     {   
19399         var _this = this;
19400         
19401         this.cells.each(function(c) {
19402             
19403             if(c.row < 5){
19404                 return;
19405             }
19406             
19407             var ev = c.events;
19408             
19409             var r = 4;
19410             if(c.row != c.events.length){
19411                 r = 4 - (4 - (c.row - c.events.length));
19412             }
19413             
19414             c.events = ev.slice(0, r);
19415             c.more = ev.slice(r);
19416             
19417             if(c.more.length && c.more.length == 1){
19418                 c.events.push(c.more.pop());
19419             }
19420             
19421             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19422             
19423         });
19424             
19425         this.cells.each(function(c) {
19426             
19427             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19428             
19429             
19430             for (var e = 0; e < c.events.length; e++){
19431                 var ev = c.events[e];
19432                 var rows = ev.rows;
19433                 
19434                 for(var i = 0; i < rows.length; i++) {
19435                 
19436                     // how many rows should it span..
19437
19438                     var  cfg = {
19439                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19440                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19441
19442                         unselectable : "on",
19443                         cn : [
19444                             {
19445                                 cls: 'fc-event-inner',
19446                                 cn : [
19447     //                                {
19448     //                                  tag:'span',
19449     //                                  cls: 'fc-event-time',
19450     //                                  html : cells.length > 1 ? '' : ev.time
19451     //                                },
19452                                     {
19453                                       tag:'span',
19454                                       cls: 'fc-event-title',
19455                                       html : String.format('{0}', ev.title)
19456                                     }
19457
19458
19459                                 ]
19460                             },
19461                             {
19462                                 cls: 'ui-resizable-handle ui-resizable-e',
19463                                 html : '&nbsp;&nbsp;&nbsp'
19464                             }
19465
19466                         ]
19467                     };
19468
19469                     if (i == 0) {
19470                         cfg.cls += ' fc-event-start';
19471                     }
19472                     if ((i+1) == rows.length) {
19473                         cfg.cls += ' fc-event-end';
19474                     }
19475
19476                     var ctr = _this.el.select('.fc-event-container',true).first();
19477                     var cg = ctr.createChild(cfg);
19478
19479                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19480                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19481
19482                     var r = (c.more.length) ? 1 : 0;
19483                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19484                     cg.setWidth(ebox.right - sbox.x -2);
19485
19486                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19487                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19488                     cg.on('click', _this.onEventClick, _this, ev);
19489
19490                     ev.els.push(cg);
19491                     
19492                 }
19493                 
19494             }
19495             
19496             
19497             if(c.more.length){
19498                 var  cfg = {
19499                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19500                     style : 'position: absolute',
19501                     unselectable : "on",
19502                     cn : [
19503                         {
19504                             cls: 'fc-event-inner',
19505                             cn : [
19506                                 {
19507                                   tag:'span',
19508                                   cls: 'fc-event-title',
19509                                   html : 'More'
19510                                 }
19511
19512
19513                             ]
19514                         },
19515                         {
19516                             cls: 'ui-resizable-handle ui-resizable-e',
19517                             html : '&nbsp;&nbsp;&nbsp'
19518                         }
19519
19520                     ]
19521                 };
19522
19523                 var ctr = _this.el.select('.fc-event-container',true).first();
19524                 var cg = ctr.createChild(cfg);
19525
19526                 var sbox = c.select('.fc-day-content',true).first().getBox();
19527                 var ebox = c.select('.fc-day-content',true).first().getBox();
19528                 //Roo.log(cg);
19529                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19530                 cg.setWidth(ebox.right - sbox.x -2);
19531
19532                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19533                 
19534             }
19535             
19536         });
19537         
19538         
19539         
19540     },
19541     
19542     onEventEnter: function (e, el,event,d) {
19543         this.fireEvent('evententer', this, el, event);
19544     },
19545     
19546     onEventLeave: function (e, el,event,d) {
19547         this.fireEvent('eventleave', this, el, event);
19548     },
19549     
19550     onEventClick: function (e, el,event,d) {
19551         this.fireEvent('eventclick', this, el, event);
19552     },
19553     
19554     onMonthChange: function () {
19555         this.store.load();
19556     },
19557     
19558     onMoreEventClick: function(e, el, more)
19559     {
19560         var _this = this;
19561         
19562         this.calpopover.placement = 'right';
19563         this.calpopover.setTitle('More');
19564         
19565         this.calpopover.setContent('');
19566         
19567         var ctr = this.calpopover.el.select('.popover-content', true).first();
19568         
19569         Roo.each(more, function(m){
19570             var cfg = {
19571                 cls : 'fc-event-hori fc-event-draggable',
19572                 html : m.title
19573             };
19574             var cg = ctr.createChild(cfg);
19575             
19576             cg.on('click', _this.onEventClick, _this, m);
19577         });
19578         
19579         this.calpopover.show(el);
19580         
19581         
19582     },
19583     
19584     onLoad: function () 
19585     {   
19586         this.calevents = [];
19587         var cal = this;
19588         
19589         if(this.store.getCount() > 0){
19590             this.store.data.each(function(d){
19591                cal.addItem({
19592                     id : d.data.id,
19593                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19594                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19595                     time : d.data.start_time,
19596                     title : d.data.title,
19597                     description : d.data.description,
19598                     venue : d.data.venue
19599                 });
19600             });
19601         }
19602         
19603         this.renderEvents();
19604         
19605         if(this.calevents.length && this.loadMask){
19606             this.maskEl.hide();
19607         }
19608     },
19609     
19610     onBeforeLoad: function()
19611     {
19612         this.clearEvents();
19613         if(this.loadMask){
19614             this.maskEl.show();
19615         }
19616     }
19617 });
19618
19619  
19620  /*
19621  * - LGPL
19622  *
19623  * element
19624  * 
19625  */
19626
19627 /**
19628  * @class Roo.bootstrap.Popover
19629  * @extends Roo.bootstrap.Component
19630  * Bootstrap Popover class
19631  * @cfg {String} html contents of the popover   (or false to use children..)
19632  * @cfg {String} title of popover (or false to hide)
19633  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19634  * @cfg {String} trigger click || hover (or false to trigger manually)
19635  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19636  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19637  *      - if false and it has a 'parent' then it will be automatically added to that element
19638  *      - if string - Roo.get  will be called 
19639  * @cfg {Number} delay - delay before showing
19640  
19641  * @constructor
19642  * Create a new Popover
19643  * @param {Object} config The config object
19644  */
19645
19646 Roo.bootstrap.Popover = function(config){
19647     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19648     
19649     this.addEvents({
19650         // raw events
19651          /**
19652          * @event show
19653          * After the popover show
19654          * 
19655          * @param {Roo.bootstrap.Popover} this
19656          */
19657         "show" : true,
19658         /**
19659          * @event hide
19660          * After the popover hide
19661          * 
19662          * @param {Roo.bootstrap.Popover} this
19663          */
19664         "hide" : true
19665     });
19666 };
19667
19668 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19669     
19670     title: false,
19671     html: false,
19672     
19673     placement : 'right',
19674     trigger : 'hover', // hover
19675     modal : false,
19676     delay : 0,
19677     
19678     over: false,
19679     
19680     can_build_overlaid : false,
19681     
19682     maskEl : false, // the mask element
19683     headerEl : false,
19684     contentEl : false,
19685     alignEl : false, // when show is called with an element - this get's stored.
19686     
19687     getChildContainer : function()
19688     {
19689         return this.contentEl;
19690         
19691     },
19692     getPopoverHeader : function()
19693     {
19694         this.title = true; // flag not to hide it..
19695         this.headerEl.addClass('p-0');
19696         return this.headerEl
19697     },
19698     
19699     
19700     getAutoCreate : function(){
19701          
19702         var cfg = {
19703            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19704            style: 'display:block',
19705            cn : [
19706                 {
19707                     cls : 'arrow'
19708                 },
19709                 {
19710                     cls : 'popover-inner ',
19711                     cn : [
19712                         {
19713                             tag: 'h3',
19714                             cls: 'popover-title popover-header',
19715                             html : this.title === false ? '' : this.title
19716                         },
19717                         {
19718                             cls : 'popover-content popover-body '  + (this.cls || ''),
19719                             html : this.html || ''
19720                         }
19721                     ]
19722                     
19723                 }
19724            ]
19725         };
19726         
19727         return cfg;
19728     },
19729     /**
19730      * @param {string} the title
19731      */
19732     setTitle: function(str)
19733     {
19734         this.title = str;
19735         if (this.el) {
19736             this.headerEl.dom.innerHTML = str;
19737         }
19738         
19739     },
19740     /**
19741      * @param {string} the body content
19742      */
19743     setContent: function(str)
19744     {
19745         this.html = str;
19746         if (this.contentEl) {
19747             this.contentEl.dom.innerHTML = str;
19748         }
19749         
19750     },
19751     // as it get's added to the bottom of the page.
19752     onRender : function(ct, position)
19753     {
19754         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19755         
19756         
19757         
19758         if(!this.el){
19759             var cfg = Roo.apply({},  this.getAutoCreate());
19760             cfg.id = Roo.id();
19761             
19762             if (this.cls) {
19763                 cfg.cls += ' ' + this.cls;
19764             }
19765             if (this.style) {
19766                 cfg.style = this.style;
19767             }
19768             //Roo.log("adding to ");
19769             this.el = Roo.get(document.body).createChild(cfg, position);
19770 //            Roo.log(this.el);
19771         }
19772         
19773         this.contentEl = this.el.select('.popover-content',true).first();
19774         this.headerEl =  this.el.select('.popover-title',true).first();
19775         
19776         var nitems = [];
19777         if(typeof(this.items) != 'undefined'){
19778             var items = this.items;
19779             delete this.items;
19780
19781             for(var i =0;i < items.length;i++) {
19782                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19783             }
19784         }
19785
19786         this.items = nitems;
19787         
19788         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19789         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19790         
19791         
19792         
19793         this.initEvents();
19794     },
19795     
19796     resizeMask : function()
19797     {
19798         this.maskEl.setSize(
19799             Roo.lib.Dom.getViewWidth(true),
19800             Roo.lib.Dom.getViewHeight(true)
19801         );
19802     },
19803     
19804     initEvents : function()
19805     {
19806         
19807         if (!this.modal) { 
19808             Roo.bootstrap.Popover.register(this);
19809         }
19810          
19811         this.arrowEl = this.el.select('.arrow',true).first();
19812         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19813         this.el.enableDisplayMode('block');
19814         this.el.hide();
19815  
19816         
19817         if (this.over === false && !this.parent()) {
19818             return; 
19819         }
19820         if (this.triggers === false) {
19821             return;
19822         }
19823          
19824         // support parent
19825         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19826         var triggers = this.trigger ? this.trigger.split(' ') : [];
19827         Roo.each(triggers, function(trigger) {
19828         
19829             if (trigger == 'click') {
19830                 on_el.on('click', this.toggle, this);
19831             } else if (trigger != 'manual') {
19832                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19833                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19834       
19835                 on_el.on(eventIn  ,this.enter, this);
19836                 on_el.on(eventOut, this.leave, this);
19837             }
19838         }, this);
19839     },
19840     
19841     
19842     // private
19843     timeout : null,
19844     hoverState : null,
19845     
19846     toggle : function () {
19847         this.hoverState == 'in' ? this.leave() : this.enter();
19848     },
19849     
19850     enter : function () {
19851         
19852         clearTimeout(this.timeout);
19853     
19854         this.hoverState = 'in';
19855     
19856         if (!this.delay || !this.delay.show) {
19857             this.show();
19858             return;
19859         }
19860         var _t = this;
19861         this.timeout = setTimeout(function () {
19862             if (_t.hoverState == 'in') {
19863                 _t.show();
19864             }
19865         }, this.delay.show)
19866     },
19867     
19868     leave : function() {
19869         clearTimeout(this.timeout);
19870     
19871         this.hoverState = 'out';
19872     
19873         if (!this.delay || !this.delay.hide) {
19874             this.hide();
19875             return;
19876         }
19877         var _t = this;
19878         this.timeout = setTimeout(function () {
19879             if (_t.hoverState == 'out') {
19880                 _t.hide();
19881             }
19882         }, this.delay.hide)
19883     },
19884     /**
19885      * Show the popover
19886      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
19887      * @param {string} (left|right|top|bottom) position
19888      */
19889     show : function (on_el, placement)
19890     {
19891         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
19892         on_el = on_el || false; // default to false
19893          
19894         if (!on_el) {
19895             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19896                 on_el = this.parent().el;
19897             } else if (this.over) {
19898                 Roo.get(this.over);
19899             }
19900             
19901         }
19902         
19903         this.alignEl = Roo.get( on_el );
19904
19905         if (!this.el) {
19906             this.render(document.body);
19907         }
19908         
19909         
19910          
19911         
19912         if (this.title === false) {
19913             this.headerEl.hide();
19914         }
19915         
19916        
19917         this.el.show();
19918         this.el.dom.style.display = 'block';
19919          
19920  
19921         if (this.alignEl) {
19922             this.updatePosition(this.placement, true);
19923              
19924         } else {
19925             // this is usually just done by the builder = to show the popoup in the middle of the scren.
19926             var es = this.el.getSize();
19927             var x = Roo.lib.Dom.getViewWidth()/2;
19928             var y = Roo.lib.Dom.getViewHeight()/2;
19929             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
19930             
19931         }
19932
19933         
19934         //var arrow = this.el.select('.arrow',true).first();
19935         //arrow.set(align[2], 
19936         
19937         this.el.addClass('in');
19938         
19939          
19940         
19941         this.hoverState = 'in';
19942         
19943         if (this.modal) {
19944             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19945             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19946             this.maskEl.dom.style.display = 'block';
19947             this.maskEl.addClass('show');
19948         }
19949         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19950  
19951         this.fireEvent('show', this);
19952         
19953     },
19954     /**
19955      * fire this manually after loading a grid in the table for example
19956      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
19957      * @param {Boolean} try and move it if we cant get right position.
19958      */
19959     updatePosition : function(placement, try_move)
19960     {
19961         // allow for calling with no parameters
19962         placement = placement   ? placement :  this.placement;
19963         try_move = typeof(try_move) == 'undefined' ? true : try_move;
19964         
19965         this.el.removeClass([
19966             'fade','top','bottom', 'left', 'right','in',
19967             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19968         ]);
19969         this.el.addClass(placement + ' bs-popover-' + placement);
19970         
19971         if (!this.alignEl ) {
19972             return false;
19973         }
19974         
19975         switch (placement) {
19976             case 'right':
19977                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
19978                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
19979                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
19980                     //normal display... or moved up/down.
19981                     this.el.setXY(offset);
19982                     var xy = this.alignEl.getAnchorXY('tr', false);
19983                     xy[0]+=2;xy[1]+=5;
19984                     this.arrowEl.setXY(xy);
19985                     return true;
19986                 }
19987                 // continue through...
19988                 return this.updatePosition('left', false);
19989                 
19990             
19991             case 'left':
19992                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
19993                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
19994                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
19995                     //normal display... or moved up/down.
19996                     this.el.setXY(offset);
19997                     var xy = this.alignEl.getAnchorXY('tl', false);
19998                     xy[0]-=10;xy[1]+=5; // << fix me
19999                     this.arrowEl.setXY(xy);
20000                     return true;
20001                 }
20002                 // call self...
20003                 return this.updatePosition('right', false);
20004             
20005             case 'top':
20006                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20007                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20008                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20009                     //normal display... or moved up/down.
20010                     this.el.setXY(offset);
20011                     var xy = this.alignEl.getAnchorXY('t', false);
20012                     xy[1]-=10; // << fix me
20013                     this.arrowEl.setXY(xy);
20014                     return true;
20015                 }
20016                 // fall through
20017                return this.updatePosition('bottom', false);
20018             
20019             case 'bottom':
20020                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20021                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20022                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20023                     //normal display... or moved up/down.
20024                     this.el.setXY(offset);
20025                     var xy = this.alignEl.getAnchorXY('b', false);
20026                      xy[1]+=2; // << fix me
20027                     this.arrowEl.setXY(xy);
20028                     return true;
20029                 }
20030                 // fall through
20031                 return this.updatePosition('top', false);
20032                 
20033             
20034         }
20035         
20036         
20037         return false;
20038     },
20039     
20040     hide : function()
20041     {
20042         this.el.setXY([0,0]);
20043         this.el.removeClass('in');
20044         this.el.hide();
20045         this.hoverState = null;
20046         this.maskEl.hide(); // always..
20047         this.fireEvent('hide', this);
20048     }
20049     
20050 });
20051
20052
20053 Roo.apply(Roo.bootstrap.Popover, {
20054
20055     alignment : {
20056         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20057         'right' : ['l-br', [10,0], 'right bs-popover-right'],
20058         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20059         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20060     },
20061     
20062     zIndex : 20001,
20063
20064     clickHander : false,
20065     
20066
20067     onMouseDown : function(e)
20068     {
20069         if (!e.getTarget(".roo-popover")) {
20070             this.hideAll();
20071         }
20072          
20073     },
20074     
20075     popups : [],
20076     
20077     register : function(popup)
20078     {
20079         if (!Roo.bootstrap.Popover.clickHandler) {
20080             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20081         }
20082         // hide other popups.
20083         this.hideAll();
20084         this.popups.push(popup);
20085     },
20086     hideAll : function()
20087     {
20088         this.popups.forEach(function(p) {
20089             p.hide();
20090         });
20091     }
20092
20093 });/*
20094  * - LGPL
20095  *
20096  * Card header - holder for the card header elements.
20097  * 
20098  */
20099
20100 /**
20101  * @class Roo.bootstrap.PopoverNav
20102  * @extends Roo.bootstrap.NavGroup
20103  * Bootstrap Popover header navigation class
20104  * @constructor
20105  * Create a new Popover Header Navigation 
20106  * @param {Object} config The config object
20107  */
20108
20109 Roo.bootstrap.PopoverNav = function(config){
20110     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20111 };
20112
20113 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
20114     
20115     
20116     container_method : 'getPopoverHeader' 
20117     
20118      
20119     
20120     
20121    
20122 });
20123
20124  
20125
20126  /*
20127  * - LGPL
20128  *
20129  * Progress
20130  * 
20131  */
20132
20133 /**
20134  * @class Roo.bootstrap.Progress
20135  * @extends Roo.bootstrap.Component
20136  * Bootstrap Progress class
20137  * @cfg {Boolean} striped striped of the progress bar
20138  * @cfg {Boolean} active animated of the progress bar
20139  * 
20140  * 
20141  * @constructor
20142  * Create a new Progress
20143  * @param {Object} config The config object
20144  */
20145
20146 Roo.bootstrap.Progress = function(config){
20147     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20148 };
20149
20150 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20151     
20152     striped : false,
20153     active: false,
20154     
20155     getAutoCreate : function(){
20156         var cfg = {
20157             tag: 'div',
20158             cls: 'progress'
20159         };
20160         
20161         
20162         if(this.striped){
20163             cfg.cls += ' progress-striped';
20164         }
20165       
20166         if(this.active){
20167             cfg.cls += ' active';
20168         }
20169         
20170         
20171         return cfg;
20172     }
20173    
20174 });
20175
20176  
20177
20178  /*
20179  * - LGPL
20180  *
20181  * ProgressBar
20182  * 
20183  */
20184
20185 /**
20186  * @class Roo.bootstrap.ProgressBar
20187  * @extends Roo.bootstrap.Component
20188  * Bootstrap ProgressBar class
20189  * @cfg {Number} aria_valuenow aria-value now
20190  * @cfg {Number} aria_valuemin aria-value min
20191  * @cfg {Number} aria_valuemax aria-value max
20192  * @cfg {String} label label for the progress bar
20193  * @cfg {String} panel (success | info | warning | danger )
20194  * @cfg {String} role role of the progress bar
20195  * @cfg {String} sr_only text
20196  * 
20197  * 
20198  * @constructor
20199  * Create a new ProgressBar
20200  * @param {Object} config The config object
20201  */
20202
20203 Roo.bootstrap.ProgressBar = function(config){
20204     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20205 };
20206
20207 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20208     
20209     aria_valuenow : 0,
20210     aria_valuemin : 0,
20211     aria_valuemax : 100,
20212     label : false,
20213     panel : false,
20214     role : false,
20215     sr_only: false,
20216     
20217     getAutoCreate : function()
20218     {
20219         
20220         var cfg = {
20221             tag: 'div',
20222             cls: 'progress-bar',
20223             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20224         };
20225         
20226         if(this.sr_only){
20227             cfg.cn = {
20228                 tag: 'span',
20229                 cls: 'sr-only',
20230                 html: this.sr_only
20231             }
20232         }
20233         
20234         if(this.role){
20235             cfg.role = this.role;
20236         }
20237         
20238         if(this.aria_valuenow){
20239             cfg['aria-valuenow'] = this.aria_valuenow;
20240         }
20241         
20242         if(this.aria_valuemin){
20243             cfg['aria-valuemin'] = this.aria_valuemin;
20244         }
20245         
20246         if(this.aria_valuemax){
20247             cfg['aria-valuemax'] = this.aria_valuemax;
20248         }
20249         
20250         if(this.label && !this.sr_only){
20251             cfg.html = this.label;
20252         }
20253         
20254         if(this.panel){
20255             cfg.cls += ' progress-bar-' + this.panel;
20256         }
20257         
20258         return cfg;
20259     },
20260     
20261     update : function(aria_valuenow)
20262     {
20263         this.aria_valuenow = aria_valuenow;
20264         
20265         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20266     }
20267    
20268 });
20269
20270  
20271
20272  /*
20273  * - LGPL
20274  *
20275  * column
20276  * 
20277  */
20278
20279 /**
20280  * @class Roo.bootstrap.TabGroup
20281  * @extends Roo.bootstrap.Column
20282  * Bootstrap Column class
20283  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20284  * @cfg {Boolean} carousel true to make the group behave like a carousel
20285  * @cfg {Boolean} bullets show bullets for the panels
20286  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20287  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20288  * @cfg {Boolean} showarrow (true|false) show arrow default true
20289  * 
20290  * @constructor
20291  * Create a new TabGroup
20292  * @param {Object} config The config object
20293  */
20294
20295 Roo.bootstrap.TabGroup = function(config){
20296     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20297     if (!this.navId) {
20298         this.navId = Roo.id();
20299     }
20300     this.tabs = [];
20301     Roo.bootstrap.TabGroup.register(this);
20302     
20303 };
20304
20305 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20306     
20307     carousel : false,
20308     transition : false,
20309     bullets : 0,
20310     timer : 0,
20311     autoslide : false,
20312     slideFn : false,
20313     slideOnTouch : false,
20314     showarrow : true,
20315     
20316     getAutoCreate : function()
20317     {
20318         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20319         
20320         cfg.cls += ' tab-content';
20321         
20322         if (this.carousel) {
20323             cfg.cls += ' carousel slide';
20324             
20325             cfg.cn = [{
20326                cls : 'carousel-inner',
20327                cn : []
20328             }];
20329         
20330             if(this.bullets  && !Roo.isTouch){
20331                 
20332                 var bullets = {
20333                     cls : 'carousel-bullets',
20334                     cn : []
20335                 };
20336                
20337                 if(this.bullets_cls){
20338                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20339                 }
20340                 
20341                 bullets.cn.push({
20342                     cls : 'clear'
20343                 });
20344                 
20345                 cfg.cn[0].cn.push(bullets);
20346             }
20347             
20348             if(this.showarrow){
20349                 cfg.cn[0].cn.push({
20350                     tag : 'div',
20351                     class : 'carousel-arrow',
20352                     cn : [
20353                         {
20354                             tag : 'div',
20355                             class : 'carousel-prev',
20356                             cn : [
20357                                 {
20358                                     tag : 'i',
20359                                     class : 'fa fa-chevron-left'
20360                                 }
20361                             ]
20362                         },
20363                         {
20364                             tag : 'div',
20365                             class : 'carousel-next',
20366                             cn : [
20367                                 {
20368                                     tag : 'i',
20369                                     class : 'fa fa-chevron-right'
20370                                 }
20371                             ]
20372                         }
20373                     ]
20374                 });
20375             }
20376             
20377         }
20378         
20379         return cfg;
20380     },
20381     
20382     initEvents:  function()
20383     {
20384 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20385 //            this.el.on("touchstart", this.onTouchStart, this);
20386 //        }
20387         
20388         if(this.autoslide){
20389             var _this = this;
20390             
20391             this.slideFn = window.setInterval(function() {
20392                 _this.showPanelNext();
20393             }, this.timer);
20394         }
20395         
20396         if(this.showarrow){
20397             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20398             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20399         }
20400         
20401         
20402     },
20403     
20404 //    onTouchStart : function(e, el, o)
20405 //    {
20406 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20407 //            return;
20408 //        }
20409 //        
20410 //        this.showPanelNext();
20411 //    },
20412     
20413     
20414     getChildContainer : function()
20415     {
20416         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20417     },
20418     
20419     /**
20420     * register a Navigation item
20421     * @param {Roo.bootstrap.NavItem} the navitem to add
20422     */
20423     register : function(item)
20424     {
20425         this.tabs.push( item);
20426         item.navId = this.navId; // not really needed..
20427         this.addBullet();
20428     
20429     },
20430     
20431     getActivePanel : function()
20432     {
20433         var r = false;
20434         Roo.each(this.tabs, function(t) {
20435             if (t.active) {
20436                 r = t;
20437                 return false;
20438             }
20439             return null;
20440         });
20441         return r;
20442         
20443     },
20444     getPanelByName : function(n)
20445     {
20446         var r = false;
20447         Roo.each(this.tabs, function(t) {
20448             if (t.tabId == n) {
20449                 r = t;
20450                 return false;
20451             }
20452             return null;
20453         });
20454         return r;
20455     },
20456     indexOfPanel : function(p)
20457     {
20458         var r = false;
20459         Roo.each(this.tabs, function(t,i) {
20460             if (t.tabId == p.tabId) {
20461                 r = i;
20462                 return false;
20463             }
20464             return null;
20465         });
20466         return r;
20467     },
20468     /**
20469      * show a specific panel
20470      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20471      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20472      */
20473     showPanel : function (pan)
20474     {
20475         if(this.transition || typeof(pan) == 'undefined'){
20476             Roo.log("waiting for the transitionend");
20477             return false;
20478         }
20479         
20480         if (typeof(pan) == 'number') {
20481             pan = this.tabs[pan];
20482         }
20483         
20484         if (typeof(pan) == 'string') {
20485             pan = this.getPanelByName(pan);
20486         }
20487         
20488         var cur = this.getActivePanel();
20489         
20490         if(!pan || !cur){
20491             Roo.log('pan or acitve pan is undefined');
20492             return false;
20493         }
20494         
20495         if (pan.tabId == this.getActivePanel().tabId) {
20496             return true;
20497         }
20498         
20499         if (false === cur.fireEvent('beforedeactivate')) {
20500             return false;
20501         }
20502         
20503         if(this.bullets > 0 && !Roo.isTouch){
20504             this.setActiveBullet(this.indexOfPanel(pan));
20505         }
20506         
20507         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20508             
20509             //class="carousel-item carousel-item-next carousel-item-left"
20510             
20511             this.transition = true;
20512             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20513             var lr = dir == 'next' ? 'left' : 'right';
20514             pan.el.addClass(dir); // or prev
20515             pan.el.addClass('carousel-item-' + dir); // or prev
20516             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20517             cur.el.addClass(lr); // or right
20518             pan.el.addClass(lr);
20519             cur.el.addClass('carousel-item-' +lr); // or right
20520             pan.el.addClass('carousel-item-' +lr);
20521             
20522             
20523             var _this = this;
20524             cur.el.on('transitionend', function() {
20525                 Roo.log("trans end?");
20526                 
20527                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20528                 pan.setActive(true);
20529                 
20530                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20531                 cur.setActive(false);
20532                 
20533                 _this.transition = false;
20534                 
20535             }, this, { single:  true } );
20536             
20537             return true;
20538         }
20539         
20540         cur.setActive(false);
20541         pan.setActive(true);
20542         
20543         return true;
20544         
20545     },
20546     showPanelNext : function()
20547     {
20548         var i = this.indexOfPanel(this.getActivePanel());
20549         
20550         if (i >= this.tabs.length - 1 && !this.autoslide) {
20551             return;
20552         }
20553         
20554         if (i >= this.tabs.length - 1 && this.autoslide) {
20555             i = -1;
20556         }
20557         
20558         this.showPanel(this.tabs[i+1]);
20559     },
20560     
20561     showPanelPrev : function()
20562     {
20563         var i = this.indexOfPanel(this.getActivePanel());
20564         
20565         if (i  < 1 && !this.autoslide) {
20566             return;
20567         }
20568         
20569         if (i < 1 && this.autoslide) {
20570             i = this.tabs.length;
20571         }
20572         
20573         this.showPanel(this.tabs[i-1]);
20574     },
20575     
20576     
20577     addBullet: function()
20578     {
20579         if(!this.bullets || Roo.isTouch){
20580             return;
20581         }
20582         var ctr = this.el.select('.carousel-bullets',true).first();
20583         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20584         var bullet = ctr.createChild({
20585             cls : 'bullet bullet-' + i
20586         },ctr.dom.lastChild);
20587         
20588         
20589         var _this = this;
20590         
20591         bullet.on('click', (function(e, el, o, ii, t){
20592
20593             e.preventDefault();
20594
20595             this.showPanel(ii);
20596
20597             if(this.autoslide && this.slideFn){
20598                 clearInterval(this.slideFn);
20599                 this.slideFn = window.setInterval(function() {
20600                     _this.showPanelNext();
20601                 }, this.timer);
20602             }
20603
20604         }).createDelegate(this, [i, bullet], true));
20605                 
20606         
20607     },
20608      
20609     setActiveBullet : function(i)
20610     {
20611         if(Roo.isTouch){
20612             return;
20613         }
20614         
20615         Roo.each(this.el.select('.bullet', true).elements, function(el){
20616             el.removeClass('selected');
20617         });
20618
20619         var bullet = this.el.select('.bullet-' + i, true).first();
20620         
20621         if(!bullet){
20622             return;
20623         }
20624         
20625         bullet.addClass('selected');
20626     }
20627     
20628     
20629   
20630 });
20631
20632  
20633
20634  
20635  
20636 Roo.apply(Roo.bootstrap.TabGroup, {
20637     
20638     groups: {},
20639      /**
20640     * register a Navigation Group
20641     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20642     */
20643     register : function(navgrp)
20644     {
20645         this.groups[navgrp.navId] = navgrp;
20646         
20647     },
20648     /**
20649     * fetch a Navigation Group based on the navigation ID
20650     * if one does not exist , it will get created.
20651     * @param {string} the navgroup to add
20652     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20653     */
20654     get: function(navId) {
20655         if (typeof(this.groups[navId]) == 'undefined') {
20656             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20657         }
20658         return this.groups[navId] ;
20659     }
20660     
20661     
20662     
20663 });
20664
20665  /*
20666  * - LGPL
20667  *
20668  * TabPanel
20669  * 
20670  */
20671
20672 /**
20673  * @class Roo.bootstrap.TabPanel
20674  * @extends Roo.bootstrap.Component
20675  * Bootstrap TabPanel class
20676  * @cfg {Boolean} active panel active
20677  * @cfg {String} html panel content
20678  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20679  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20680  * @cfg {String} href click to link..
20681  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20682  * 
20683  * 
20684  * @constructor
20685  * Create a new TabPanel
20686  * @param {Object} config The config object
20687  */
20688
20689 Roo.bootstrap.TabPanel = function(config){
20690     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20691     this.addEvents({
20692         /**
20693              * @event changed
20694              * Fires when the active status changes
20695              * @param {Roo.bootstrap.TabPanel} this
20696              * @param {Boolean} state the new state
20697             
20698          */
20699         'changed': true,
20700         /**
20701              * @event beforedeactivate
20702              * Fires before a tab is de-activated - can be used to do validation on a form.
20703              * @param {Roo.bootstrap.TabPanel} this
20704              * @return {Boolean} false if there is an error
20705             
20706          */
20707         'beforedeactivate': true
20708      });
20709     
20710     this.tabId = this.tabId || Roo.id();
20711   
20712 };
20713
20714 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20715     
20716     active: false,
20717     html: false,
20718     tabId: false,
20719     navId : false,
20720     href : '',
20721     touchSlide : false,
20722     getAutoCreate : function(){
20723         
20724         
20725         var cfg = {
20726             tag: 'div',
20727             // item is needed for carousel - not sure if it has any effect otherwise
20728             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20729             html: this.html || ''
20730         };
20731         
20732         if(this.active){
20733             cfg.cls += ' active';
20734         }
20735         
20736         if(this.tabId){
20737             cfg.tabId = this.tabId;
20738         }
20739         
20740         
20741         
20742         return cfg;
20743     },
20744     
20745     initEvents:  function()
20746     {
20747         var p = this.parent();
20748         
20749         this.navId = this.navId || p.navId;
20750         
20751         if (typeof(this.navId) != 'undefined') {
20752             // not really needed.. but just in case.. parent should be a NavGroup.
20753             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20754             
20755             tg.register(this);
20756             
20757             var i = tg.tabs.length - 1;
20758             
20759             if(this.active && tg.bullets > 0 && i < tg.bullets){
20760                 tg.setActiveBullet(i);
20761             }
20762         }
20763         
20764         this.el.on('click', this.onClick, this);
20765         
20766         if(Roo.isTouch && this.touchSlide){
20767             this.el.on("touchstart", this.onTouchStart, this);
20768             this.el.on("touchmove", this.onTouchMove, this);
20769             this.el.on("touchend", this.onTouchEnd, this);
20770         }
20771         
20772     },
20773     
20774     onRender : function(ct, position)
20775     {
20776         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20777     },
20778     
20779     setActive : function(state)
20780     {
20781         Roo.log("panel - set active " + this.tabId + "=" + state);
20782         
20783         this.active = state;
20784         if (!state) {
20785             this.el.removeClass('active');
20786             
20787         } else  if (!this.el.hasClass('active')) {
20788             this.el.addClass('active');
20789         }
20790         
20791         this.fireEvent('changed', this, state);
20792     },
20793     
20794     onClick : function(e)
20795     {
20796         e.preventDefault();
20797         
20798         if(!this.href.length){
20799             return;
20800         }
20801         
20802         window.location.href = this.href;
20803     },
20804     
20805     startX : 0,
20806     startY : 0,
20807     endX : 0,
20808     endY : 0,
20809     swiping : false,
20810     
20811     onTouchStart : function(e)
20812     {
20813         this.swiping = false;
20814         
20815         this.startX = e.browserEvent.touches[0].clientX;
20816         this.startY = e.browserEvent.touches[0].clientY;
20817     },
20818     
20819     onTouchMove : function(e)
20820     {
20821         this.swiping = true;
20822         
20823         this.endX = e.browserEvent.touches[0].clientX;
20824         this.endY = e.browserEvent.touches[0].clientY;
20825     },
20826     
20827     onTouchEnd : function(e)
20828     {
20829         if(!this.swiping){
20830             this.onClick(e);
20831             return;
20832         }
20833         
20834         var tabGroup = this.parent();
20835         
20836         if(this.endX > this.startX){ // swiping right
20837             tabGroup.showPanelPrev();
20838             return;
20839         }
20840         
20841         if(this.startX > this.endX){ // swiping left
20842             tabGroup.showPanelNext();
20843             return;
20844         }
20845     }
20846     
20847     
20848 });
20849  
20850
20851  
20852
20853  /*
20854  * - LGPL
20855  *
20856  * DateField
20857  * 
20858  */
20859
20860 /**
20861  * @class Roo.bootstrap.DateField
20862  * @extends Roo.bootstrap.Input
20863  * Bootstrap DateField class
20864  * @cfg {Number} weekStart default 0
20865  * @cfg {String} viewMode default empty, (months|years)
20866  * @cfg {String} minViewMode default empty, (months|years)
20867  * @cfg {Number} startDate default -Infinity
20868  * @cfg {Number} endDate default Infinity
20869  * @cfg {Boolean} todayHighlight default false
20870  * @cfg {Boolean} todayBtn default false
20871  * @cfg {Boolean} calendarWeeks default false
20872  * @cfg {Object} daysOfWeekDisabled default empty
20873  * @cfg {Boolean} singleMode default false (true | false)
20874  * 
20875  * @cfg {Boolean} keyboardNavigation default true
20876  * @cfg {String} language default en
20877  * 
20878  * @constructor
20879  * Create a new DateField
20880  * @param {Object} config The config object
20881  */
20882
20883 Roo.bootstrap.DateField = function(config){
20884     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20885      this.addEvents({
20886             /**
20887              * @event show
20888              * Fires when this field show.
20889              * @param {Roo.bootstrap.DateField} this
20890              * @param {Mixed} date The date value
20891              */
20892             show : true,
20893             /**
20894              * @event show
20895              * Fires when this field hide.
20896              * @param {Roo.bootstrap.DateField} this
20897              * @param {Mixed} date The date value
20898              */
20899             hide : true,
20900             /**
20901              * @event select
20902              * Fires when select a date.
20903              * @param {Roo.bootstrap.DateField} this
20904              * @param {Mixed} date The date value
20905              */
20906             select : true,
20907             /**
20908              * @event beforeselect
20909              * Fires when before select a date.
20910              * @param {Roo.bootstrap.DateField} this
20911              * @param {Mixed} date The date value
20912              */
20913             beforeselect : true
20914         });
20915 };
20916
20917 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20918     
20919     /**
20920      * @cfg {String} format
20921      * The default date format string which can be overriden for localization support.  The format must be
20922      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20923      */
20924     format : "m/d/y",
20925     /**
20926      * @cfg {String} altFormats
20927      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20928      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20929      */
20930     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20931     
20932     weekStart : 0,
20933     
20934     viewMode : '',
20935     
20936     minViewMode : '',
20937     
20938     todayHighlight : false,
20939     
20940     todayBtn: false,
20941     
20942     language: 'en',
20943     
20944     keyboardNavigation: true,
20945     
20946     calendarWeeks: false,
20947     
20948     startDate: -Infinity,
20949     
20950     endDate: Infinity,
20951     
20952     daysOfWeekDisabled: [],
20953     
20954     _events: [],
20955     
20956     singleMode : false,
20957     
20958     UTCDate: function()
20959     {
20960         return new Date(Date.UTC.apply(Date, arguments));
20961     },
20962     
20963     UTCToday: function()
20964     {
20965         var today = new Date();
20966         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20967     },
20968     
20969     getDate: function() {
20970             var d = this.getUTCDate();
20971             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20972     },
20973     
20974     getUTCDate: function() {
20975             return this.date;
20976     },
20977     
20978     setDate: function(d) {
20979             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20980     },
20981     
20982     setUTCDate: function(d) {
20983             this.date = d;
20984             this.setValue(this.formatDate(this.date));
20985     },
20986         
20987     onRender: function(ct, position)
20988     {
20989         
20990         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20991         
20992         this.language = this.language || 'en';
20993         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20994         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20995         
20996         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20997         this.format = this.format || 'm/d/y';
20998         this.isInline = false;
20999         this.isInput = true;
21000         this.component = this.el.select('.add-on', true).first() || false;
21001         this.component = (this.component && this.component.length === 0) ? false : this.component;
21002         this.hasInput = this.component && this.inputEl().length;
21003         
21004         if (typeof(this.minViewMode === 'string')) {
21005             switch (this.minViewMode) {
21006                 case 'months':
21007                     this.minViewMode = 1;
21008                     break;
21009                 case 'years':
21010                     this.minViewMode = 2;
21011                     break;
21012                 default:
21013                     this.minViewMode = 0;
21014                     break;
21015             }
21016         }
21017         
21018         if (typeof(this.viewMode === 'string')) {
21019             switch (this.viewMode) {
21020                 case 'months':
21021                     this.viewMode = 1;
21022                     break;
21023                 case 'years':
21024                     this.viewMode = 2;
21025                     break;
21026                 default:
21027                     this.viewMode = 0;
21028                     break;
21029             }
21030         }
21031                 
21032         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21033         
21034 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21035         
21036         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21037         
21038         this.picker().on('mousedown', this.onMousedown, this);
21039         this.picker().on('click', this.onClick, this);
21040         
21041         this.picker().addClass('datepicker-dropdown');
21042         
21043         this.startViewMode = this.viewMode;
21044         
21045         if(this.singleMode){
21046             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21047                 v.setVisibilityMode(Roo.Element.DISPLAY);
21048                 v.hide();
21049             });
21050             
21051             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21052                 v.setStyle('width', '189px');
21053             });
21054         }
21055         
21056         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21057             if(!this.calendarWeeks){
21058                 v.remove();
21059                 return;
21060             }
21061             
21062             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21063             v.attr('colspan', function(i, val){
21064                 return parseInt(val) + 1;
21065             });
21066         });
21067                         
21068         
21069         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21070         
21071         this.setStartDate(this.startDate);
21072         this.setEndDate(this.endDate);
21073         
21074         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21075         
21076         this.fillDow();
21077         this.fillMonths();
21078         this.update();
21079         this.showMode();
21080         
21081         if(this.isInline) {
21082             this.showPopup();
21083         }
21084     },
21085     
21086     picker : function()
21087     {
21088         return this.pickerEl;
21089 //        return this.el.select('.datepicker', true).first();
21090     },
21091     
21092     fillDow: function()
21093     {
21094         var dowCnt = this.weekStart;
21095         
21096         var dow = {
21097             tag: 'tr',
21098             cn: [
21099                 
21100             ]
21101         };
21102         
21103         if(this.calendarWeeks){
21104             dow.cn.push({
21105                 tag: 'th',
21106                 cls: 'cw',
21107                 html: '&nbsp;'
21108             })
21109         }
21110         
21111         while (dowCnt < this.weekStart + 7) {
21112             dow.cn.push({
21113                 tag: 'th',
21114                 cls: 'dow',
21115                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21116             });
21117         }
21118         
21119         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21120     },
21121     
21122     fillMonths: function()
21123     {    
21124         var i = 0;
21125         var months = this.picker().select('>.datepicker-months td', true).first();
21126         
21127         months.dom.innerHTML = '';
21128         
21129         while (i < 12) {
21130             var month = {
21131                 tag: 'span',
21132                 cls: 'month',
21133                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21134             };
21135             
21136             months.createChild(month);
21137         }
21138         
21139     },
21140     
21141     update: function()
21142     {
21143         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;
21144         
21145         if (this.date < this.startDate) {
21146             this.viewDate = new Date(this.startDate);
21147         } else if (this.date > this.endDate) {
21148             this.viewDate = new Date(this.endDate);
21149         } else {
21150             this.viewDate = new Date(this.date);
21151         }
21152         
21153         this.fill();
21154     },
21155     
21156     fill: function() 
21157     {
21158         var d = new Date(this.viewDate),
21159                 year = d.getUTCFullYear(),
21160                 month = d.getUTCMonth(),
21161                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21162                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21163                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21164                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21165                 currentDate = this.date && this.date.valueOf(),
21166                 today = this.UTCToday();
21167         
21168         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21169         
21170 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21171         
21172 //        this.picker.select('>tfoot th.today').
21173 //                                              .text(dates[this.language].today)
21174 //                                              .toggle(this.todayBtn !== false);
21175     
21176         this.updateNavArrows();
21177         this.fillMonths();
21178                                                 
21179         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21180         
21181         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21182          
21183         prevMonth.setUTCDate(day);
21184         
21185         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21186         
21187         var nextMonth = new Date(prevMonth);
21188         
21189         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21190         
21191         nextMonth = nextMonth.valueOf();
21192         
21193         var fillMonths = false;
21194         
21195         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21196         
21197         while(prevMonth.valueOf() <= nextMonth) {
21198             var clsName = '';
21199             
21200             if (prevMonth.getUTCDay() === this.weekStart) {
21201                 if(fillMonths){
21202                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21203                 }
21204                     
21205                 fillMonths = {
21206                     tag: 'tr',
21207                     cn: []
21208                 };
21209                 
21210                 if(this.calendarWeeks){
21211                     // ISO 8601: First week contains first thursday.
21212                     // ISO also states week starts on Monday, but we can be more abstract here.
21213                     var
21214                     // Start of current week: based on weekstart/current date
21215                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21216                     // Thursday of this week
21217                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21218                     // First Thursday of year, year from thursday
21219                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21220                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21221                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21222                     
21223                     fillMonths.cn.push({
21224                         tag: 'td',
21225                         cls: 'cw',
21226                         html: calWeek
21227                     });
21228                 }
21229             }
21230             
21231             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21232                 clsName += ' old';
21233             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21234                 clsName += ' new';
21235             }
21236             if (this.todayHighlight &&
21237                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21238                 prevMonth.getUTCMonth() == today.getMonth() &&
21239                 prevMonth.getUTCDate() == today.getDate()) {
21240                 clsName += ' today';
21241             }
21242             
21243             if (currentDate && prevMonth.valueOf() === currentDate) {
21244                 clsName += ' active';
21245             }
21246             
21247             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21248                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21249                     clsName += ' disabled';
21250             }
21251             
21252             fillMonths.cn.push({
21253                 tag: 'td',
21254                 cls: 'day ' + clsName,
21255                 html: prevMonth.getDate()
21256             });
21257             
21258             prevMonth.setDate(prevMonth.getDate()+1);
21259         }
21260           
21261         var currentYear = this.date && this.date.getUTCFullYear();
21262         var currentMonth = this.date && this.date.getUTCMonth();
21263         
21264         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21265         
21266         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21267             v.removeClass('active');
21268             
21269             if(currentYear === year && k === currentMonth){
21270                 v.addClass('active');
21271             }
21272             
21273             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21274                 v.addClass('disabled');
21275             }
21276             
21277         });
21278         
21279         
21280         year = parseInt(year/10, 10) * 10;
21281         
21282         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21283         
21284         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21285         
21286         year -= 1;
21287         for (var i = -1; i < 11; i++) {
21288             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21289                 tag: 'span',
21290                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21291                 html: year
21292             });
21293             
21294             year += 1;
21295         }
21296     },
21297     
21298     showMode: function(dir) 
21299     {
21300         if (dir) {
21301             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21302         }
21303         
21304         Roo.each(this.picker().select('>div',true).elements, function(v){
21305             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21306             v.hide();
21307         });
21308         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21309     },
21310     
21311     place: function()
21312     {
21313         if(this.isInline) {
21314             return;
21315         }
21316         
21317         this.picker().removeClass(['bottom', 'top']);
21318         
21319         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21320             /*
21321              * place to the top of element!
21322              *
21323              */
21324             
21325             this.picker().addClass('top');
21326             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21327             
21328             return;
21329         }
21330         
21331         this.picker().addClass('bottom');
21332         
21333         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21334     },
21335     
21336     parseDate : function(value)
21337     {
21338         if(!value || value instanceof Date){
21339             return value;
21340         }
21341         var v = Date.parseDate(value, this.format);
21342         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21343             v = Date.parseDate(value, 'Y-m-d');
21344         }
21345         if(!v && this.altFormats){
21346             if(!this.altFormatsArray){
21347                 this.altFormatsArray = this.altFormats.split("|");
21348             }
21349             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21350                 v = Date.parseDate(value, this.altFormatsArray[i]);
21351             }
21352         }
21353         return v;
21354     },
21355     
21356     formatDate : function(date, fmt)
21357     {   
21358         return (!date || !(date instanceof Date)) ?
21359         date : date.dateFormat(fmt || this.format);
21360     },
21361     
21362     onFocus : function()
21363     {
21364         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21365         this.showPopup();
21366     },
21367     
21368     onBlur : function()
21369     {
21370         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21371         
21372         var d = this.inputEl().getValue();
21373         
21374         this.setValue(d);
21375                 
21376         this.hidePopup();
21377     },
21378     
21379     showPopup : function()
21380     {
21381         this.picker().show();
21382         this.update();
21383         this.place();
21384         
21385         this.fireEvent('showpopup', this, this.date);
21386     },
21387     
21388     hidePopup : function()
21389     {
21390         if(this.isInline) {
21391             return;
21392         }
21393         this.picker().hide();
21394         this.viewMode = this.startViewMode;
21395         this.showMode();
21396         
21397         this.fireEvent('hidepopup', this, this.date);
21398         
21399     },
21400     
21401     onMousedown: function(e)
21402     {
21403         e.stopPropagation();
21404         e.preventDefault();
21405     },
21406     
21407     keyup: function(e)
21408     {
21409         Roo.bootstrap.DateField.superclass.keyup.call(this);
21410         this.update();
21411     },
21412
21413     setValue: function(v)
21414     {
21415         if(this.fireEvent('beforeselect', this, v) !== false){
21416             var d = new Date(this.parseDate(v) ).clearTime();
21417         
21418             if(isNaN(d.getTime())){
21419                 this.date = this.viewDate = '';
21420                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21421                 return;
21422             }
21423
21424             v = this.formatDate(d);
21425
21426             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21427
21428             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21429
21430             this.update();
21431
21432             this.fireEvent('select', this, this.date);
21433         }
21434     },
21435     
21436     getValue: function()
21437     {
21438         return this.formatDate(this.date);
21439     },
21440     
21441     fireKey: function(e)
21442     {
21443         if (!this.picker().isVisible()){
21444             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21445                 this.showPopup();
21446             }
21447             return;
21448         }
21449         
21450         var dateChanged = false,
21451         dir, day, month,
21452         newDate, newViewDate;
21453         
21454         switch(e.keyCode){
21455             case 27: // escape
21456                 this.hidePopup();
21457                 e.preventDefault();
21458                 break;
21459             case 37: // left
21460             case 39: // right
21461                 if (!this.keyboardNavigation) {
21462                     break;
21463                 }
21464                 dir = e.keyCode == 37 ? -1 : 1;
21465                 
21466                 if (e.ctrlKey){
21467                     newDate = this.moveYear(this.date, dir);
21468                     newViewDate = this.moveYear(this.viewDate, dir);
21469                 } else if (e.shiftKey){
21470                     newDate = this.moveMonth(this.date, dir);
21471                     newViewDate = this.moveMonth(this.viewDate, dir);
21472                 } else {
21473                     newDate = new Date(this.date);
21474                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21475                     newViewDate = new Date(this.viewDate);
21476                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21477                 }
21478                 if (this.dateWithinRange(newDate)){
21479                     this.date = newDate;
21480                     this.viewDate = newViewDate;
21481                     this.setValue(this.formatDate(this.date));
21482 //                    this.update();
21483                     e.preventDefault();
21484                     dateChanged = true;
21485                 }
21486                 break;
21487             case 38: // up
21488             case 40: // down
21489                 if (!this.keyboardNavigation) {
21490                     break;
21491                 }
21492                 dir = e.keyCode == 38 ? -1 : 1;
21493                 if (e.ctrlKey){
21494                     newDate = this.moveYear(this.date, dir);
21495                     newViewDate = this.moveYear(this.viewDate, dir);
21496                 } else if (e.shiftKey){
21497                     newDate = this.moveMonth(this.date, dir);
21498                     newViewDate = this.moveMonth(this.viewDate, dir);
21499                 } else {
21500                     newDate = new Date(this.date);
21501                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21502                     newViewDate = new Date(this.viewDate);
21503                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21504                 }
21505                 if (this.dateWithinRange(newDate)){
21506                     this.date = newDate;
21507                     this.viewDate = newViewDate;
21508                     this.setValue(this.formatDate(this.date));
21509 //                    this.update();
21510                     e.preventDefault();
21511                     dateChanged = true;
21512                 }
21513                 break;
21514             case 13: // enter
21515                 this.setValue(this.formatDate(this.date));
21516                 this.hidePopup();
21517                 e.preventDefault();
21518                 break;
21519             case 9: // tab
21520                 this.setValue(this.formatDate(this.date));
21521                 this.hidePopup();
21522                 break;
21523             case 16: // shift
21524             case 17: // ctrl
21525             case 18: // alt
21526                 break;
21527             default :
21528                 this.hidePopup();
21529                 
21530         }
21531     },
21532     
21533     
21534     onClick: function(e) 
21535     {
21536         e.stopPropagation();
21537         e.preventDefault();
21538         
21539         var target = e.getTarget();
21540         
21541         if(target.nodeName.toLowerCase() === 'i'){
21542             target = Roo.get(target).dom.parentNode;
21543         }
21544         
21545         var nodeName = target.nodeName;
21546         var className = target.className;
21547         var html = target.innerHTML;
21548         //Roo.log(nodeName);
21549         
21550         switch(nodeName.toLowerCase()) {
21551             case 'th':
21552                 switch(className) {
21553                     case 'switch':
21554                         this.showMode(1);
21555                         break;
21556                     case 'prev':
21557                     case 'next':
21558                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21559                         switch(this.viewMode){
21560                                 case 0:
21561                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21562                                         break;
21563                                 case 1:
21564                                 case 2:
21565                                         this.viewDate = this.moveYear(this.viewDate, dir);
21566                                         break;
21567                         }
21568                         this.fill();
21569                         break;
21570                     case 'today':
21571                         var date = new Date();
21572                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21573 //                        this.fill()
21574                         this.setValue(this.formatDate(this.date));
21575                         
21576                         this.hidePopup();
21577                         break;
21578                 }
21579                 break;
21580             case 'span':
21581                 if (className.indexOf('disabled') < 0) {
21582                     this.viewDate.setUTCDate(1);
21583                     if (className.indexOf('month') > -1) {
21584                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21585                     } else {
21586                         var year = parseInt(html, 10) || 0;
21587                         this.viewDate.setUTCFullYear(year);
21588                         
21589                     }
21590                     
21591                     if(this.singleMode){
21592                         this.setValue(this.formatDate(this.viewDate));
21593                         this.hidePopup();
21594                         return;
21595                     }
21596                     
21597                     this.showMode(-1);
21598                     this.fill();
21599                 }
21600                 break;
21601                 
21602             case 'td':
21603                 //Roo.log(className);
21604                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21605                     var day = parseInt(html, 10) || 1;
21606                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
21607                         month = (this.viewDate || new Date()).getUTCMonth();
21608
21609                     if (className.indexOf('old') > -1) {
21610                         if(month === 0 ){
21611                             month = 11;
21612                             year -= 1;
21613                         }else{
21614                             month -= 1;
21615                         }
21616                     } else if (className.indexOf('new') > -1) {
21617                         if (month == 11) {
21618                             month = 0;
21619                             year += 1;
21620                         } else {
21621                             month += 1;
21622                         }
21623                     }
21624                     //Roo.log([year,month,day]);
21625                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21626                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21627 //                    this.fill();
21628                     //Roo.log(this.formatDate(this.date));
21629                     this.setValue(this.formatDate(this.date));
21630                     this.hidePopup();
21631                 }
21632                 break;
21633         }
21634     },
21635     
21636     setStartDate: function(startDate)
21637     {
21638         this.startDate = startDate || -Infinity;
21639         if (this.startDate !== -Infinity) {
21640             this.startDate = this.parseDate(this.startDate);
21641         }
21642         this.update();
21643         this.updateNavArrows();
21644     },
21645
21646     setEndDate: function(endDate)
21647     {
21648         this.endDate = endDate || Infinity;
21649         if (this.endDate !== Infinity) {
21650             this.endDate = this.parseDate(this.endDate);
21651         }
21652         this.update();
21653         this.updateNavArrows();
21654     },
21655     
21656     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21657     {
21658         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21659         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21660             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21661         }
21662         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21663             return parseInt(d, 10);
21664         });
21665         this.update();
21666         this.updateNavArrows();
21667     },
21668     
21669     updateNavArrows: function() 
21670     {
21671         if(this.singleMode){
21672             return;
21673         }
21674         
21675         var d = new Date(this.viewDate),
21676         year = d.getUTCFullYear(),
21677         month = d.getUTCMonth();
21678         
21679         Roo.each(this.picker().select('.prev', true).elements, function(v){
21680             v.show();
21681             switch (this.viewMode) {
21682                 case 0:
21683
21684                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21685                         v.hide();
21686                     }
21687                     break;
21688                 case 1:
21689                 case 2:
21690                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21691                         v.hide();
21692                     }
21693                     break;
21694             }
21695         });
21696         
21697         Roo.each(this.picker().select('.next', true).elements, function(v){
21698             v.show();
21699             switch (this.viewMode) {
21700                 case 0:
21701
21702                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21703                         v.hide();
21704                     }
21705                     break;
21706                 case 1:
21707                 case 2:
21708                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21709                         v.hide();
21710                     }
21711                     break;
21712             }
21713         })
21714     },
21715     
21716     moveMonth: function(date, dir)
21717     {
21718         if (!dir) {
21719             return date;
21720         }
21721         var new_date = new Date(date.valueOf()),
21722         day = new_date.getUTCDate(),
21723         month = new_date.getUTCMonth(),
21724         mag = Math.abs(dir),
21725         new_month, test;
21726         dir = dir > 0 ? 1 : -1;
21727         if (mag == 1){
21728             test = dir == -1
21729             // If going back one month, make sure month is not current month
21730             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21731             ? function(){
21732                 return new_date.getUTCMonth() == month;
21733             }
21734             // If going forward one month, make sure month is as expected
21735             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21736             : function(){
21737                 return new_date.getUTCMonth() != new_month;
21738             };
21739             new_month = month + dir;
21740             new_date.setUTCMonth(new_month);
21741             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21742             if (new_month < 0 || new_month > 11) {
21743                 new_month = (new_month + 12) % 12;
21744             }
21745         } else {
21746             // For magnitudes >1, move one month at a time...
21747             for (var i=0; i<mag; i++) {
21748                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21749                 new_date = this.moveMonth(new_date, dir);
21750             }
21751             // ...then reset the day, keeping it in the new month
21752             new_month = new_date.getUTCMonth();
21753             new_date.setUTCDate(day);
21754             test = function(){
21755                 return new_month != new_date.getUTCMonth();
21756             };
21757         }
21758         // Common date-resetting loop -- if date is beyond end of month, make it
21759         // end of month
21760         while (test()){
21761             new_date.setUTCDate(--day);
21762             new_date.setUTCMonth(new_month);
21763         }
21764         return new_date;
21765     },
21766
21767     moveYear: function(date, dir)
21768     {
21769         return this.moveMonth(date, dir*12);
21770     },
21771
21772     dateWithinRange: function(date)
21773     {
21774         return date >= this.startDate && date <= this.endDate;
21775     },
21776
21777     
21778     remove: function() 
21779     {
21780         this.picker().remove();
21781     },
21782     
21783     validateValue : function(value)
21784     {
21785         if(this.getVisibilityEl().hasClass('hidden')){
21786             return true;
21787         }
21788         
21789         if(value.length < 1)  {
21790             if(this.allowBlank){
21791                 return true;
21792             }
21793             return false;
21794         }
21795         
21796         if(value.length < this.minLength){
21797             return false;
21798         }
21799         if(value.length > this.maxLength){
21800             return false;
21801         }
21802         if(this.vtype){
21803             var vt = Roo.form.VTypes;
21804             if(!vt[this.vtype](value, this)){
21805                 return false;
21806             }
21807         }
21808         if(typeof this.validator == "function"){
21809             var msg = this.validator(value);
21810             if(msg !== true){
21811                 return false;
21812             }
21813         }
21814         
21815         if(this.regex && !this.regex.test(value)){
21816             return false;
21817         }
21818         
21819         if(typeof(this.parseDate(value)) == 'undefined'){
21820             return false;
21821         }
21822         
21823         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21824             return false;
21825         }      
21826         
21827         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21828             return false;
21829         } 
21830         
21831         
21832         return true;
21833     },
21834     
21835     reset : function()
21836     {
21837         this.date = this.viewDate = '';
21838         
21839         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21840     }
21841    
21842 });
21843
21844 Roo.apply(Roo.bootstrap.DateField,  {
21845     
21846     head : {
21847         tag: 'thead',
21848         cn: [
21849         {
21850             tag: 'tr',
21851             cn: [
21852             {
21853                 tag: 'th',
21854                 cls: 'prev',
21855                 html: '<i class="fa fa-arrow-left"/>'
21856             },
21857             {
21858                 tag: 'th',
21859                 cls: 'switch',
21860                 colspan: '5'
21861             },
21862             {
21863                 tag: 'th',
21864                 cls: 'next',
21865                 html: '<i class="fa fa-arrow-right"/>'
21866             }
21867
21868             ]
21869         }
21870         ]
21871     },
21872     
21873     content : {
21874         tag: 'tbody',
21875         cn: [
21876         {
21877             tag: 'tr',
21878             cn: [
21879             {
21880                 tag: 'td',
21881                 colspan: '7'
21882             }
21883             ]
21884         }
21885         ]
21886     },
21887     
21888     footer : {
21889         tag: 'tfoot',
21890         cn: [
21891         {
21892             tag: 'tr',
21893             cn: [
21894             {
21895                 tag: 'th',
21896                 colspan: '7',
21897                 cls: 'today'
21898             }
21899                     
21900             ]
21901         }
21902         ]
21903     },
21904     
21905     dates:{
21906         en: {
21907             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21908             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21909             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21910             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21911             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21912             today: "Today"
21913         }
21914     },
21915     
21916     modes: [
21917     {
21918         clsName: 'days',
21919         navFnc: 'Month',
21920         navStep: 1
21921     },
21922     {
21923         clsName: 'months',
21924         navFnc: 'FullYear',
21925         navStep: 1
21926     },
21927     {
21928         clsName: 'years',
21929         navFnc: 'FullYear',
21930         navStep: 10
21931     }]
21932 });
21933
21934 Roo.apply(Roo.bootstrap.DateField,  {
21935   
21936     template : {
21937         tag: 'div',
21938         cls: 'datepicker dropdown-menu roo-dynamic shadow',
21939         cn: [
21940         {
21941             tag: 'div',
21942             cls: 'datepicker-days',
21943             cn: [
21944             {
21945                 tag: 'table',
21946                 cls: 'table-condensed',
21947                 cn:[
21948                 Roo.bootstrap.DateField.head,
21949                 {
21950                     tag: 'tbody'
21951                 },
21952                 Roo.bootstrap.DateField.footer
21953                 ]
21954             }
21955             ]
21956         },
21957         {
21958             tag: 'div',
21959             cls: 'datepicker-months',
21960             cn: [
21961             {
21962                 tag: 'table',
21963                 cls: 'table-condensed',
21964                 cn:[
21965                 Roo.bootstrap.DateField.head,
21966                 Roo.bootstrap.DateField.content,
21967                 Roo.bootstrap.DateField.footer
21968                 ]
21969             }
21970             ]
21971         },
21972         {
21973             tag: 'div',
21974             cls: 'datepicker-years',
21975             cn: [
21976             {
21977                 tag: 'table',
21978                 cls: 'table-condensed',
21979                 cn:[
21980                 Roo.bootstrap.DateField.head,
21981                 Roo.bootstrap.DateField.content,
21982                 Roo.bootstrap.DateField.footer
21983                 ]
21984             }
21985             ]
21986         }
21987         ]
21988     }
21989 });
21990
21991  
21992
21993  /*
21994  * - LGPL
21995  *
21996  * TimeField
21997  * 
21998  */
21999
22000 /**
22001  * @class Roo.bootstrap.TimeField
22002  * @extends Roo.bootstrap.Input
22003  * Bootstrap DateField class
22004  * 
22005  * 
22006  * @constructor
22007  * Create a new TimeField
22008  * @param {Object} config The config object
22009  */
22010
22011 Roo.bootstrap.TimeField = function(config){
22012     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22013     this.addEvents({
22014             /**
22015              * @event show
22016              * Fires when this field show.
22017              * @param {Roo.bootstrap.DateField} thisthis
22018              * @param {Mixed} date The date value
22019              */
22020             show : true,
22021             /**
22022              * @event show
22023              * Fires when this field hide.
22024              * @param {Roo.bootstrap.DateField} this
22025              * @param {Mixed} date The date value
22026              */
22027             hide : true,
22028             /**
22029              * @event select
22030              * Fires when select a date.
22031              * @param {Roo.bootstrap.DateField} this
22032              * @param {Mixed} date The date value
22033              */
22034             select : true
22035         });
22036 };
22037
22038 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
22039     
22040     /**
22041      * @cfg {String} format
22042      * The default time format string which can be overriden for localization support.  The format must be
22043      * valid according to {@link Date#parseDate} (defaults to 'H:i').
22044      */
22045     format : "H:i",
22046
22047     getAutoCreate : function()
22048     {
22049         this.after = '<i class="fa far fa-clock"></i>';
22050         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22051         
22052          
22053     },
22054     onRender: function(ct, position)
22055     {
22056         
22057         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22058                 
22059         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22060         
22061         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22062         
22063         this.pop = this.picker().select('>.datepicker-time',true).first();
22064         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22065         
22066         this.picker().on('mousedown', this.onMousedown, this);
22067         this.picker().on('click', this.onClick, this);
22068         
22069         this.picker().addClass('datepicker-dropdown');
22070     
22071         this.fillTime();
22072         this.update();
22073             
22074         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22075         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22076         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22077         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22078         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22079         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22080
22081     },
22082     
22083     fireKey: function(e){
22084         if (!this.picker().isVisible()){
22085             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22086                 this.show();
22087             }
22088             return;
22089         }
22090
22091         e.preventDefault();
22092         
22093         switch(e.keyCode){
22094             case 27: // escape
22095                 this.hide();
22096                 break;
22097             case 37: // left
22098             case 39: // right
22099                 this.onTogglePeriod();
22100                 break;
22101             case 38: // up
22102                 this.onIncrementMinutes();
22103                 break;
22104             case 40: // down
22105                 this.onDecrementMinutes();
22106                 break;
22107             case 13: // enter
22108             case 9: // tab
22109                 this.setTime();
22110                 break;
22111         }
22112     },
22113     
22114     onClick: function(e) {
22115         e.stopPropagation();
22116         e.preventDefault();
22117     },
22118     
22119     picker : function()
22120     {
22121         return this.pickerEl;
22122     },
22123     
22124     fillTime: function()
22125     {    
22126         var time = this.pop.select('tbody', true).first();
22127         
22128         time.dom.innerHTML = '';
22129         
22130         time.createChild({
22131             tag: 'tr',
22132             cn: [
22133                 {
22134                     tag: 'td',
22135                     cn: [
22136                         {
22137                             tag: 'a',
22138                             href: '#',
22139                             cls: 'btn',
22140                             cn: [
22141                                 {
22142                                     tag: 'i',
22143                                     cls: 'hours-up fa fas fa-chevron-up'
22144                                 }
22145                             ]
22146                         } 
22147                     ]
22148                 },
22149                 {
22150                     tag: 'td',
22151                     cls: 'separator'
22152                 },
22153                 {
22154                     tag: 'td',
22155                     cn: [
22156                         {
22157                             tag: 'a',
22158                             href: '#',
22159                             cls: 'btn',
22160                             cn: [
22161                                 {
22162                                     tag: 'i',
22163                                     cls: 'minutes-up fa fas fa-chevron-up'
22164                                 }
22165                             ]
22166                         }
22167                     ]
22168                 },
22169                 {
22170                     tag: 'td',
22171                     cls: 'separator'
22172                 }
22173             ]
22174         });
22175         
22176         time.createChild({
22177             tag: 'tr',
22178             cn: [
22179                 {
22180                     tag: 'td',
22181                     cn: [
22182                         {
22183                             tag: 'span',
22184                             cls: 'timepicker-hour',
22185                             html: '00'
22186                         }  
22187                     ]
22188                 },
22189                 {
22190                     tag: 'td',
22191                     cls: 'separator',
22192                     html: ':'
22193                 },
22194                 {
22195                     tag: 'td',
22196                     cn: [
22197                         {
22198                             tag: 'span',
22199                             cls: 'timepicker-minute',
22200                             html: '00'
22201                         }  
22202                     ]
22203                 },
22204                 {
22205                     tag: 'td',
22206                     cls: 'separator'
22207                 },
22208                 {
22209                     tag: 'td',
22210                     cn: [
22211                         {
22212                             tag: 'button',
22213                             type: 'button',
22214                             cls: 'btn btn-primary period',
22215                             html: 'AM'
22216                             
22217                         }
22218                     ]
22219                 }
22220             ]
22221         });
22222         
22223         time.createChild({
22224             tag: 'tr',
22225             cn: [
22226                 {
22227                     tag: 'td',
22228                     cn: [
22229                         {
22230                             tag: 'a',
22231                             href: '#',
22232                             cls: 'btn',
22233                             cn: [
22234                                 {
22235                                     tag: 'span',
22236                                     cls: 'hours-down fa fas fa-chevron-down'
22237                                 }
22238                             ]
22239                         }
22240                     ]
22241                 },
22242                 {
22243                     tag: 'td',
22244                     cls: 'separator'
22245                 },
22246                 {
22247                     tag: 'td',
22248                     cn: [
22249                         {
22250                             tag: 'a',
22251                             href: '#',
22252                             cls: 'btn',
22253                             cn: [
22254                                 {
22255                                     tag: 'span',
22256                                     cls: 'minutes-down fa fas fa-chevron-down'
22257                                 }
22258                             ]
22259                         }
22260                     ]
22261                 },
22262                 {
22263                     tag: 'td',
22264                     cls: 'separator'
22265                 }
22266             ]
22267         });
22268         
22269     },
22270     
22271     update: function()
22272     {
22273         
22274         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22275         
22276         this.fill();
22277     },
22278     
22279     fill: function() 
22280     {
22281         var hours = this.time.getHours();
22282         var minutes = this.time.getMinutes();
22283         var period = 'AM';
22284         
22285         if(hours > 11){
22286             period = 'PM';
22287         }
22288         
22289         if(hours == 0){
22290             hours = 12;
22291         }
22292         
22293         
22294         if(hours > 12){
22295             hours = hours - 12;
22296         }
22297         
22298         if(hours < 10){
22299             hours = '0' + hours;
22300         }
22301         
22302         if(minutes < 10){
22303             minutes = '0' + minutes;
22304         }
22305         
22306         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22307         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22308         this.pop.select('button', true).first().dom.innerHTML = period;
22309         
22310     },
22311     
22312     place: function()
22313     {   
22314         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22315         
22316         var cls = ['bottom'];
22317         
22318         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22319             cls.pop();
22320             cls.push('top');
22321         }
22322         
22323         cls.push('right');
22324         
22325         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22326             cls.pop();
22327             cls.push('left');
22328         }
22329         //this.picker().setXY(20000,20000);
22330         this.picker().addClass(cls.join('-'));
22331         
22332         var _this = this;
22333         
22334         Roo.each(cls, function(c){
22335             if(c == 'bottom'){
22336                 (function() {
22337                  //  
22338                 }).defer(200);
22339                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
22340                 //_this.picker().setTop(_this.inputEl().getHeight());
22341                 return;
22342             }
22343             if(c == 'top'){
22344                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
22345                 
22346                 //_this.picker().setTop(0 - _this.picker().getHeight());
22347                 return;
22348             }
22349             /*
22350             if(c == 'left'){
22351                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22352                 return;
22353             }
22354             if(c == 'right'){
22355                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22356                 return;
22357             }
22358             */
22359         });
22360         
22361     },
22362   
22363     onFocus : function()
22364     {
22365         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22366         this.show();
22367     },
22368     
22369     onBlur : function()
22370     {
22371         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22372         this.hide();
22373     },
22374     
22375     show : function()
22376     {
22377         this.picker().show();
22378         this.pop.show();
22379         this.update();
22380         this.place();
22381         
22382         this.fireEvent('show', this, this.date);
22383     },
22384     
22385     hide : function()
22386     {
22387         this.picker().hide();
22388         this.pop.hide();
22389         
22390         this.fireEvent('hide', this, this.date);
22391     },
22392     
22393     setTime : function()
22394     {
22395         this.hide();
22396         this.setValue(this.time.format(this.format));
22397         
22398         this.fireEvent('select', this, this.date);
22399         
22400         
22401     },
22402     
22403     onMousedown: function(e){
22404         e.stopPropagation();
22405         e.preventDefault();
22406     },
22407     
22408     onIncrementHours: function()
22409     {
22410         Roo.log('onIncrementHours');
22411         this.time = this.time.add(Date.HOUR, 1);
22412         this.update();
22413         
22414     },
22415     
22416     onDecrementHours: function()
22417     {
22418         Roo.log('onDecrementHours');
22419         this.time = this.time.add(Date.HOUR, -1);
22420         this.update();
22421     },
22422     
22423     onIncrementMinutes: function()
22424     {
22425         Roo.log('onIncrementMinutes');
22426         this.time = this.time.add(Date.MINUTE, 1);
22427         this.update();
22428     },
22429     
22430     onDecrementMinutes: function()
22431     {
22432         Roo.log('onDecrementMinutes');
22433         this.time = this.time.add(Date.MINUTE, -1);
22434         this.update();
22435     },
22436     
22437     onTogglePeriod: function()
22438     {
22439         Roo.log('onTogglePeriod');
22440         this.time = this.time.add(Date.HOUR, 12);
22441         this.update();
22442     }
22443     
22444    
22445 });
22446  
22447
22448 Roo.apply(Roo.bootstrap.TimeField,  {
22449   
22450     template : {
22451         tag: 'div',
22452         cls: 'datepicker dropdown-menu',
22453         cn: [
22454             {
22455                 tag: 'div',
22456                 cls: 'datepicker-time',
22457                 cn: [
22458                 {
22459                     tag: 'table',
22460                     cls: 'table-condensed',
22461                     cn:[
22462                         {
22463                             tag: 'tbody',
22464                             cn: [
22465                                 {
22466                                     tag: 'tr',
22467                                     cn: [
22468                                     {
22469                                         tag: 'td',
22470                                         colspan: '7'
22471                                     }
22472                                     ]
22473                                 }
22474                             ]
22475                         },
22476                         {
22477                             tag: 'tfoot',
22478                             cn: [
22479                                 {
22480                                     tag: 'tr',
22481                                     cn: [
22482                                     {
22483                                         tag: 'th',
22484                                         colspan: '7',
22485                                         cls: '',
22486                                         cn: [
22487                                             {
22488                                                 tag: 'button',
22489                                                 cls: 'btn btn-info ok',
22490                                                 html: 'OK'
22491                                             }
22492                                         ]
22493                                     }
22494                     
22495                                     ]
22496                                 }
22497                             ]
22498                         }
22499                     ]
22500                 }
22501                 ]
22502             }
22503         ]
22504     }
22505 });
22506
22507  
22508
22509  /*
22510  * - LGPL
22511  *
22512  * MonthField
22513  * 
22514  */
22515
22516 /**
22517  * @class Roo.bootstrap.MonthField
22518  * @extends Roo.bootstrap.Input
22519  * Bootstrap MonthField class
22520  * 
22521  * @cfg {String} language default en
22522  * 
22523  * @constructor
22524  * Create a new MonthField
22525  * @param {Object} config The config object
22526  */
22527
22528 Roo.bootstrap.MonthField = function(config){
22529     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22530     
22531     this.addEvents({
22532         /**
22533          * @event show
22534          * Fires when this field show.
22535          * @param {Roo.bootstrap.MonthField} this
22536          * @param {Mixed} date The date value
22537          */
22538         show : true,
22539         /**
22540          * @event show
22541          * Fires when this field hide.
22542          * @param {Roo.bootstrap.MonthField} this
22543          * @param {Mixed} date The date value
22544          */
22545         hide : true,
22546         /**
22547          * @event select
22548          * Fires when select a date.
22549          * @param {Roo.bootstrap.MonthField} this
22550          * @param {String} oldvalue The old value
22551          * @param {String} newvalue The new value
22552          */
22553         select : true
22554     });
22555 };
22556
22557 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22558     
22559     onRender: function(ct, position)
22560     {
22561         
22562         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22563         
22564         this.language = this.language || 'en';
22565         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22566         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22567         
22568         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22569         this.isInline = false;
22570         this.isInput = true;
22571         this.component = this.el.select('.add-on', true).first() || false;
22572         this.component = (this.component && this.component.length === 0) ? false : this.component;
22573         this.hasInput = this.component && this.inputEL().length;
22574         
22575         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22576         
22577         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22578         
22579         this.picker().on('mousedown', this.onMousedown, this);
22580         this.picker().on('click', this.onClick, this);
22581         
22582         this.picker().addClass('datepicker-dropdown');
22583         
22584         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22585             v.setStyle('width', '189px');
22586         });
22587         
22588         this.fillMonths();
22589         
22590         this.update();
22591         
22592         if(this.isInline) {
22593             this.show();
22594         }
22595         
22596     },
22597     
22598     setValue: function(v, suppressEvent)
22599     {   
22600         var o = this.getValue();
22601         
22602         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22603         
22604         this.update();
22605
22606         if(suppressEvent !== true){
22607             this.fireEvent('select', this, o, v);
22608         }
22609         
22610     },
22611     
22612     getValue: function()
22613     {
22614         return this.value;
22615     },
22616     
22617     onClick: function(e) 
22618     {
22619         e.stopPropagation();
22620         e.preventDefault();
22621         
22622         var target = e.getTarget();
22623         
22624         if(target.nodeName.toLowerCase() === 'i'){
22625             target = Roo.get(target).dom.parentNode;
22626         }
22627         
22628         var nodeName = target.nodeName;
22629         var className = target.className;
22630         var html = target.innerHTML;
22631         
22632         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22633             return;
22634         }
22635         
22636         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22637         
22638         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22639         
22640         this.hide();
22641                         
22642     },
22643     
22644     picker : function()
22645     {
22646         return this.pickerEl;
22647     },
22648     
22649     fillMonths: function()
22650     {    
22651         var i = 0;
22652         var months = this.picker().select('>.datepicker-months td', true).first();
22653         
22654         months.dom.innerHTML = '';
22655         
22656         while (i < 12) {
22657             var month = {
22658                 tag: 'span',
22659                 cls: 'month',
22660                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22661             };
22662             
22663             months.createChild(month);
22664         }
22665         
22666     },
22667     
22668     update: function()
22669     {
22670         var _this = this;
22671         
22672         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22673             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22674         }
22675         
22676         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22677             e.removeClass('active');
22678             
22679             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22680                 e.addClass('active');
22681             }
22682         })
22683     },
22684     
22685     place: function()
22686     {
22687         if(this.isInline) {
22688             return;
22689         }
22690         
22691         this.picker().removeClass(['bottom', 'top']);
22692         
22693         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22694             /*
22695              * place to the top of element!
22696              *
22697              */
22698             
22699             this.picker().addClass('top');
22700             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22701             
22702             return;
22703         }
22704         
22705         this.picker().addClass('bottom');
22706         
22707         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22708     },
22709     
22710     onFocus : function()
22711     {
22712         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22713         this.show();
22714     },
22715     
22716     onBlur : function()
22717     {
22718         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22719         
22720         var d = this.inputEl().getValue();
22721         
22722         this.setValue(d);
22723                 
22724         this.hide();
22725     },
22726     
22727     show : function()
22728     {
22729         this.picker().show();
22730         this.picker().select('>.datepicker-months', true).first().show();
22731         this.update();
22732         this.place();
22733         
22734         this.fireEvent('show', this, this.date);
22735     },
22736     
22737     hide : function()
22738     {
22739         if(this.isInline) {
22740             return;
22741         }
22742         this.picker().hide();
22743         this.fireEvent('hide', this, this.date);
22744         
22745     },
22746     
22747     onMousedown: function(e)
22748     {
22749         e.stopPropagation();
22750         e.preventDefault();
22751     },
22752     
22753     keyup: function(e)
22754     {
22755         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22756         this.update();
22757     },
22758
22759     fireKey: function(e)
22760     {
22761         if (!this.picker().isVisible()){
22762             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22763                 this.show();
22764             }
22765             return;
22766         }
22767         
22768         var dir;
22769         
22770         switch(e.keyCode){
22771             case 27: // escape
22772                 this.hide();
22773                 e.preventDefault();
22774                 break;
22775             case 37: // left
22776             case 39: // right
22777                 dir = e.keyCode == 37 ? -1 : 1;
22778                 
22779                 this.vIndex = this.vIndex + dir;
22780                 
22781                 if(this.vIndex < 0){
22782                     this.vIndex = 0;
22783                 }
22784                 
22785                 if(this.vIndex > 11){
22786                     this.vIndex = 11;
22787                 }
22788                 
22789                 if(isNaN(this.vIndex)){
22790                     this.vIndex = 0;
22791                 }
22792                 
22793                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22794                 
22795                 break;
22796             case 38: // up
22797             case 40: // down
22798                 
22799                 dir = e.keyCode == 38 ? -1 : 1;
22800                 
22801                 this.vIndex = this.vIndex + dir * 4;
22802                 
22803                 if(this.vIndex < 0){
22804                     this.vIndex = 0;
22805                 }
22806                 
22807                 if(this.vIndex > 11){
22808                     this.vIndex = 11;
22809                 }
22810                 
22811                 if(isNaN(this.vIndex)){
22812                     this.vIndex = 0;
22813                 }
22814                 
22815                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22816                 break;
22817                 
22818             case 13: // enter
22819                 
22820                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22821                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22822                 }
22823                 
22824                 this.hide();
22825                 e.preventDefault();
22826                 break;
22827             case 9: // tab
22828                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22829                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22830                 }
22831                 this.hide();
22832                 break;
22833             case 16: // shift
22834             case 17: // ctrl
22835             case 18: // alt
22836                 break;
22837             default :
22838                 this.hide();
22839                 
22840         }
22841     },
22842     
22843     remove: function() 
22844     {
22845         this.picker().remove();
22846     }
22847    
22848 });
22849
22850 Roo.apply(Roo.bootstrap.MonthField,  {
22851     
22852     content : {
22853         tag: 'tbody',
22854         cn: [
22855         {
22856             tag: 'tr',
22857             cn: [
22858             {
22859                 tag: 'td',
22860                 colspan: '7'
22861             }
22862             ]
22863         }
22864         ]
22865     },
22866     
22867     dates:{
22868         en: {
22869             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22870             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22871         }
22872     }
22873 });
22874
22875 Roo.apply(Roo.bootstrap.MonthField,  {
22876   
22877     template : {
22878         tag: 'div',
22879         cls: 'datepicker dropdown-menu roo-dynamic',
22880         cn: [
22881             {
22882                 tag: 'div',
22883                 cls: 'datepicker-months',
22884                 cn: [
22885                 {
22886                     tag: 'table',
22887                     cls: 'table-condensed',
22888                     cn:[
22889                         Roo.bootstrap.DateField.content
22890                     ]
22891                 }
22892                 ]
22893             }
22894         ]
22895     }
22896 });
22897
22898  
22899
22900  
22901  /*
22902  * - LGPL
22903  *
22904  * CheckBox
22905  * 
22906  */
22907
22908 /**
22909  * @class Roo.bootstrap.CheckBox
22910  * @extends Roo.bootstrap.Input
22911  * Bootstrap CheckBox class
22912  * 
22913  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22914  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22915  * @cfg {String} boxLabel The text that appears beside the checkbox
22916  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22917  * @cfg {Boolean} checked initnal the element
22918  * @cfg {Boolean} inline inline the element (default false)
22919  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22920  * @cfg {String} tooltip label tooltip
22921  * 
22922  * @constructor
22923  * Create a new CheckBox
22924  * @param {Object} config The config object
22925  */
22926
22927 Roo.bootstrap.CheckBox = function(config){
22928     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22929    
22930     this.addEvents({
22931         /**
22932         * @event check
22933         * Fires when the element is checked or unchecked.
22934         * @param {Roo.bootstrap.CheckBox} this This input
22935         * @param {Boolean} checked The new checked value
22936         */
22937        check : true,
22938        /**
22939         * @event click
22940         * Fires when the element is click.
22941         * @param {Roo.bootstrap.CheckBox} this This input
22942         */
22943        click : true
22944     });
22945     
22946 };
22947
22948 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
22949   
22950     inputType: 'checkbox',
22951     inputValue: 1,
22952     valueOff: 0,
22953     boxLabel: false,
22954     checked: false,
22955     weight : false,
22956     inline: false,
22957     tooltip : '',
22958     
22959     // checkbox success does not make any sense really.. 
22960     invalidClass : "",
22961     validClass : "",
22962     
22963     
22964     getAutoCreate : function()
22965     {
22966         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22967         
22968         var id = Roo.id();
22969         
22970         var cfg = {};
22971         
22972         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22973         
22974         if(this.inline){
22975             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
22976         }
22977         
22978         var input =  {
22979             tag: 'input',
22980             id : id,
22981             type : this.inputType,
22982             value : this.inputValue,
22983             cls : 'roo-' + this.inputType, //'form-box',
22984             placeholder : this.placeholder || ''
22985             
22986         };
22987         
22988         if(this.inputType != 'radio'){
22989             var hidden =  {
22990                 tag: 'input',
22991                 type : 'hidden',
22992                 cls : 'roo-hidden-value',
22993                 value : this.checked ? this.inputValue : this.valueOff
22994             };
22995         }
22996         
22997             
22998         if (this.weight) { // Validity check?
22999             cfg.cls += " " + this.inputType + "-" + this.weight;
23000         }
23001         
23002         if (this.disabled) {
23003             input.disabled=true;
23004         }
23005         
23006         if(this.checked){
23007             input.checked = this.checked;
23008         }
23009         
23010         if (this.name) {
23011             
23012             input.name = this.name;
23013             
23014             if(this.inputType != 'radio'){
23015                 hidden.name = this.name;
23016                 input.name = '_hidden_' + this.name;
23017             }
23018         }
23019         
23020         if (this.size) {
23021             input.cls += ' input-' + this.size;
23022         }
23023         
23024         var settings=this;
23025         
23026         ['xs','sm','md','lg'].map(function(size){
23027             if (settings[size]) {
23028                 cfg.cls += ' col-' + size + '-' + settings[size];
23029             }
23030         });
23031         
23032         var inputblock = input;
23033          
23034         if (this.before || this.after) {
23035             
23036             inputblock = {
23037                 cls : 'input-group',
23038                 cn :  [] 
23039             };
23040             
23041             if (this.before) {
23042                 inputblock.cn.push({
23043                     tag :'span',
23044                     cls : 'input-group-addon',
23045                     html : this.before
23046                 });
23047             }
23048             
23049             inputblock.cn.push(input);
23050             
23051             if(this.inputType != 'radio'){
23052                 inputblock.cn.push(hidden);
23053             }
23054             
23055             if (this.after) {
23056                 inputblock.cn.push({
23057                     tag :'span',
23058                     cls : 'input-group-addon',
23059                     html : this.after
23060                 });
23061             }
23062             
23063         }
23064         var boxLabelCfg = false;
23065         
23066         if(this.boxLabel){
23067            
23068             boxLabelCfg = {
23069                 tag: 'label',
23070                 //'for': id, // box label is handled by onclick - so no for...
23071                 cls: 'box-label',
23072                 html: this.boxLabel
23073             };
23074             if(this.tooltip){
23075                 boxLabelCfg.tooltip = this.tooltip;
23076             }
23077              
23078         }
23079         
23080         
23081         if (align ==='left' && this.fieldLabel.length) {
23082 //                Roo.log("left and has label");
23083             cfg.cn = [
23084                 {
23085                     tag: 'label',
23086                     'for' :  id,
23087                     cls : 'control-label',
23088                     html : this.fieldLabel
23089                 },
23090                 {
23091                     cls : "", 
23092                     cn: [
23093                         inputblock
23094                     ]
23095                 }
23096             ];
23097             
23098             if (boxLabelCfg) {
23099                 cfg.cn[1].cn.push(boxLabelCfg);
23100             }
23101             
23102             if(this.labelWidth > 12){
23103                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23104             }
23105             
23106             if(this.labelWidth < 13 && this.labelmd == 0){
23107                 this.labelmd = this.labelWidth;
23108             }
23109             
23110             if(this.labellg > 0){
23111                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23112                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23113             }
23114             
23115             if(this.labelmd > 0){
23116                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23117                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23118             }
23119             
23120             if(this.labelsm > 0){
23121                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23122                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23123             }
23124             
23125             if(this.labelxs > 0){
23126                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23127                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23128             }
23129             
23130         } else if ( this.fieldLabel.length) {
23131 //                Roo.log(" label");
23132                 cfg.cn = [
23133                    
23134                     {
23135                         tag: this.boxLabel ? 'span' : 'label',
23136                         'for': id,
23137                         cls: 'control-label box-input-label',
23138                         //cls : 'input-group-addon',
23139                         html : this.fieldLabel
23140                     },
23141                     
23142                     inputblock
23143                     
23144                 ];
23145                 if (boxLabelCfg) {
23146                     cfg.cn.push(boxLabelCfg);
23147                 }
23148
23149         } else {
23150             
23151 //                Roo.log(" no label && no align");
23152                 cfg.cn = [  inputblock ] ;
23153                 if (boxLabelCfg) {
23154                     cfg.cn.push(boxLabelCfg);
23155                 }
23156
23157                 
23158         }
23159         
23160        
23161         
23162         if(this.inputType != 'radio'){
23163             cfg.cn.push(hidden);
23164         }
23165         
23166         return cfg;
23167         
23168     },
23169     
23170     /**
23171      * return the real input element.
23172      */
23173     inputEl: function ()
23174     {
23175         return this.el.select('input.roo-' + this.inputType,true).first();
23176     },
23177     hiddenEl: function ()
23178     {
23179         return this.el.select('input.roo-hidden-value',true).first();
23180     },
23181     
23182     labelEl: function()
23183     {
23184         return this.el.select('label.control-label',true).first();
23185     },
23186     /* depricated... */
23187     
23188     label: function()
23189     {
23190         return this.labelEl();
23191     },
23192     
23193     boxLabelEl: function()
23194     {
23195         return this.el.select('label.box-label',true).first();
23196     },
23197     
23198     initEvents : function()
23199     {
23200 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23201         
23202         this.inputEl().on('click', this.onClick,  this);
23203         
23204         if (this.boxLabel) { 
23205             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23206         }
23207         
23208         this.startValue = this.getValue();
23209         
23210         if(this.groupId){
23211             Roo.bootstrap.CheckBox.register(this);
23212         }
23213     },
23214     
23215     onClick : function(e)
23216     {   
23217         if(this.fireEvent('click', this, e) !== false){
23218             this.setChecked(!this.checked);
23219         }
23220         
23221     },
23222     
23223     setChecked : function(state,suppressEvent)
23224     {
23225         this.startValue = this.getValue();
23226
23227         if(this.inputType == 'radio'){
23228             
23229             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23230                 e.dom.checked = false;
23231             });
23232             
23233             this.inputEl().dom.checked = true;
23234             
23235             this.inputEl().dom.value = this.inputValue;
23236             
23237             if(suppressEvent !== true){
23238                 this.fireEvent('check', this, true);
23239             }
23240             
23241             this.validate();
23242             
23243             return;
23244         }
23245         
23246         this.checked = state;
23247         
23248         this.inputEl().dom.checked = state;
23249         
23250         
23251         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23252         
23253         if(suppressEvent !== true){
23254             this.fireEvent('check', this, state);
23255         }
23256         
23257         this.validate();
23258     },
23259     
23260     getValue : function()
23261     {
23262         if(this.inputType == 'radio'){
23263             return this.getGroupValue();
23264         }
23265         
23266         return this.hiddenEl().dom.value;
23267         
23268     },
23269     
23270     getGroupValue : function()
23271     {
23272         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23273             return '';
23274         }
23275         
23276         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23277     },
23278     
23279     setValue : function(v,suppressEvent)
23280     {
23281         if(this.inputType == 'radio'){
23282             this.setGroupValue(v, suppressEvent);
23283             return;
23284         }
23285         
23286         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23287         
23288         this.validate();
23289     },
23290     
23291     setGroupValue : function(v, suppressEvent)
23292     {
23293         this.startValue = this.getValue();
23294         
23295         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23296             e.dom.checked = false;
23297             
23298             if(e.dom.value == v){
23299                 e.dom.checked = true;
23300             }
23301         });
23302         
23303         if(suppressEvent !== true){
23304             this.fireEvent('check', this, true);
23305         }
23306
23307         this.validate();
23308         
23309         return;
23310     },
23311     
23312     validate : function()
23313     {
23314         if(this.getVisibilityEl().hasClass('hidden')){
23315             return true;
23316         }
23317         
23318         if(
23319                 this.disabled || 
23320                 (this.inputType == 'radio' && this.validateRadio()) ||
23321                 (this.inputType == 'checkbox' && this.validateCheckbox())
23322         ){
23323             this.markValid();
23324             return true;
23325         }
23326         
23327         this.markInvalid();
23328         return false;
23329     },
23330     
23331     validateRadio : function()
23332     {
23333         if(this.getVisibilityEl().hasClass('hidden')){
23334             return true;
23335         }
23336         
23337         if(this.allowBlank){
23338             return true;
23339         }
23340         
23341         var valid = false;
23342         
23343         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23344             if(!e.dom.checked){
23345                 return;
23346             }
23347             
23348             valid = true;
23349             
23350             return false;
23351         });
23352         
23353         return valid;
23354     },
23355     
23356     validateCheckbox : function()
23357     {
23358         if(!this.groupId){
23359             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23360             //return (this.getValue() == this.inputValue) ? true : false;
23361         }
23362         
23363         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23364         
23365         if(!group){
23366             return false;
23367         }
23368         
23369         var r = false;
23370         
23371         for(var i in group){
23372             if(group[i].el.isVisible(true)){
23373                 r = false;
23374                 break;
23375             }
23376             
23377             r = true;
23378         }
23379         
23380         for(var i in group){
23381             if(r){
23382                 break;
23383             }
23384             
23385             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23386         }
23387         
23388         return r;
23389     },
23390     
23391     /**
23392      * Mark this field as valid
23393      */
23394     markValid : function()
23395     {
23396         var _this = this;
23397         
23398         this.fireEvent('valid', this);
23399         
23400         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23401         
23402         if(this.groupId){
23403             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23404         }
23405         
23406         if(label){
23407             label.markValid();
23408         }
23409
23410         if(this.inputType == 'radio'){
23411             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23412                 var fg = e.findParent('.form-group', false, true);
23413                 if (Roo.bootstrap.version == 3) {
23414                     fg.removeClass([_this.invalidClass, _this.validClass]);
23415                     fg.addClass(_this.validClass);
23416                 } else {
23417                     fg.removeClass(['is-valid', 'is-invalid']);
23418                     fg.addClass('is-valid');
23419                 }
23420             });
23421             
23422             return;
23423         }
23424
23425         if(!this.groupId){
23426             var fg = this.el.findParent('.form-group', false, true);
23427             if (Roo.bootstrap.version == 3) {
23428                 fg.removeClass([this.invalidClass, this.validClass]);
23429                 fg.addClass(this.validClass);
23430             } else {
23431                 fg.removeClass(['is-valid', 'is-invalid']);
23432                 fg.addClass('is-valid');
23433             }
23434             return;
23435         }
23436         
23437         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23438         
23439         if(!group){
23440             return;
23441         }
23442         
23443         for(var i in group){
23444             var fg = group[i].el.findParent('.form-group', false, true);
23445             if (Roo.bootstrap.version == 3) {
23446                 fg.removeClass([this.invalidClass, this.validClass]);
23447                 fg.addClass(this.validClass);
23448             } else {
23449                 fg.removeClass(['is-valid', 'is-invalid']);
23450                 fg.addClass('is-valid');
23451             }
23452         }
23453     },
23454     
23455      /**
23456      * Mark this field as invalid
23457      * @param {String} msg The validation message
23458      */
23459     markInvalid : function(msg)
23460     {
23461         if(this.allowBlank){
23462             return;
23463         }
23464         
23465         var _this = this;
23466         
23467         this.fireEvent('invalid', this, msg);
23468         
23469         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23470         
23471         if(this.groupId){
23472             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23473         }
23474         
23475         if(label){
23476             label.markInvalid();
23477         }
23478             
23479         if(this.inputType == 'radio'){
23480             
23481             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23482                 var fg = e.findParent('.form-group', false, true);
23483                 if (Roo.bootstrap.version == 3) {
23484                     fg.removeClass([_this.invalidClass, _this.validClass]);
23485                     fg.addClass(_this.invalidClass);
23486                 } else {
23487                     fg.removeClass(['is-invalid', 'is-valid']);
23488                     fg.addClass('is-invalid');
23489                 }
23490             });
23491             
23492             return;
23493         }
23494         
23495         if(!this.groupId){
23496             var fg = this.el.findParent('.form-group', false, true);
23497             if (Roo.bootstrap.version == 3) {
23498                 fg.removeClass([_this.invalidClass, _this.validClass]);
23499                 fg.addClass(_this.invalidClass);
23500             } else {
23501                 fg.removeClass(['is-invalid', 'is-valid']);
23502                 fg.addClass('is-invalid');
23503             }
23504             return;
23505         }
23506         
23507         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23508         
23509         if(!group){
23510             return;
23511         }
23512         
23513         for(var i in group){
23514             var fg = group[i].el.findParent('.form-group', false, true);
23515             if (Roo.bootstrap.version == 3) {
23516                 fg.removeClass([_this.invalidClass, _this.validClass]);
23517                 fg.addClass(_this.invalidClass);
23518             } else {
23519                 fg.removeClass(['is-invalid', 'is-valid']);
23520                 fg.addClass('is-invalid');
23521             }
23522         }
23523         
23524     },
23525     
23526     clearInvalid : function()
23527     {
23528         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23529         
23530         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23531         
23532         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23533         
23534         if (label && label.iconEl) {
23535             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23536             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23537         }
23538     },
23539     
23540     disable : function()
23541     {
23542         if(this.inputType != 'radio'){
23543             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23544             return;
23545         }
23546         
23547         var _this = this;
23548         
23549         if(this.rendered){
23550             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23551                 _this.getActionEl().addClass(this.disabledClass);
23552                 e.dom.disabled = true;
23553             });
23554         }
23555         
23556         this.disabled = true;
23557         this.fireEvent("disable", this);
23558         return this;
23559     },
23560
23561     enable : function()
23562     {
23563         if(this.inputType != 'radio'){
23564             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23565             return;
23566         }
23567         
23568         var _this = this;
23569         
23570         if(this.rendered){
23571             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23572                 _this.getActionEl().removeClass(this.disabledClass);
23573                 e.dom.disabled = false;
23574             });
23575         }
23576         
23577         this.disabled = false;
23578         this.fireEvent("enable", this);
23579         return this;
23580     },
23581     
23582     setBoxLabel : function(v)
23583     {
23584         this.boxLabel = v;
23585         
23586         if(this.rendered){
23587             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23588         }
23589     }
23590
23591 });
23592
23593 Roo.apply(Roo.bootstrap.CheckBox, {
23594     
23595     groups: {},
23596     
23597      /**
23598     * register a CheckBox Group
23599     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23600     */
23601     register : function(checkbox)
23602     {
23603         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23604             this.groups[checkbox.groupId] = {};
23605         }
23606         
23607         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23608             return;
23609         }
23610         
23611         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23612         
23613     },
23614     /**
23615     * fetch a CheckBox Group based on the group ID
23616     * @param {string} the group ID
23617     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23618     */
23619     get: function(groupId) {
23620         if (typeof(this.groups[groupId]) == 'undefined') {
23621             return false;
23622         }
23623         
23624         return this.groups[groupId] ;
23625     }
23626     
23627     
23628 });
23629 /*
23630  * - LGPL
23631  *
23632  * RadioItem
23633  * 
23634  */
23635
23636 /**
23637  * @class Roo.bootstrap.Radio
23638  * @extends Roo.bootstrap.Component
23639  * Bootstrap Radio class
23640  * @cfg {String} boxLabel - the label associated
23641  * @cfg {String} value - the value of radio
23642  * 
23643  * @constructor
23644  * Create a new Radio
23645  * @param {Object} config The config object
23646  */
23647 Roo.bootstrap.Radio = function(config){
23648     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23649     
23650 };
23651
23652 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23653     
23654     boxLabel : '',
23655     
23656     value : '',
23657     
23658     getAutoCreate : function()
23659     {
23660         var cfg = {
23661             tag : 'div',
23662             cls : 'form-group radio',
23663             cn : [
23664                 {
23665                     tag : 'label',
23666                     cls : 'box-label',
23667                     html : this.boxLabel
23668                 }
23669             ]
23670         };
23671         
23672         return cfg;
23673     },
23674     
23675     initEvents : function() 
23676     {
23677         this.parent().register(this);
23678         
23679         this.el.on('click', this.onClick, this);
23680         
23681     },
23682     
23683     onClick : function(e)
23684     {
23685         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23686             this.setChecked(true);
23687         }
23688     },
23689     
23690     setChecked : function(state, suppressEvent)
23691     {
23692         this.parent().setValue(this.value, suppressEvent);
23693         
23694     },
23695     
23696     setBoxLabel : function(v)
23697     {
23698         this.boxLabel = v;
23699         
23700         if(this.rendered){
23701             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23702         }
23703     }
23704     
23705 });
23706  
23707
23708  /*
23709  * - LGPL
23710  *
23711  * Input
23712  * 
23713  */
23714
23715 /**
23716  * @class Roo.bootstrap.SecurePass
23717  * @extends Roo.bootstrap.Input
23718  * Bootstrap SecurePass class
23719  *
23720  * 
23721  * @constructor
23722  * Create a new SecurePass
23723  * @param {Object} config The config object
23724  */
23725  
23726 Roo.bootstrap.SecurePass = function (config) {
23727     // these go here, so the translation tool can replace them..
23728     this.errors = {
23729         PwdEmpty: "Please type a password, and then retype it to confirm.",
23730         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23731         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23732         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23733         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23734         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23735         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23736         TooWeak: "Your password is Too Weak."
23737     },
23738     this.meterLabel = "Password strength:";
23739     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23740     this.meterClass = [
23741         "roo-password-meter-tooweak", 
23742         "roo-password-meter-weak", 
23743         "roo-password-meter-medium", 
23744         "roo-password-meter-strong", 
23745         "roo-password-meter-grey"
23746     ];
23747     
23748     this.errors = {};
23749     
23750     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23751 }
23752
23753 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23754     /**
23755      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23756      * {
23757      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23758      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23759      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23760      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23761      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23762      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23763      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23764      * })
23765      */
23766     // private
23767     
23768     meterWidth: 300,
23769     errorMsg :'',    
23770     errors: false,
23771     imageRoot: '/',
23772     /**
23773      * @cfg {String/Object} Label for the strength meter (defaults to
23774      * 'Password strength:')
23775      */
23776     // private
23777     meterLabel: '',
23778     /**
23779      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23780      * ['Weak', 'Medium', 'Strong'])
23781      */
23782     // private    
23783     pwdStrengths: false,    
23784     // private
23785     strength: 0,
23786     // private
23787     _lastPwd: null,
23788     // private
23789     kCapitalLetter: 0,
23790     kSmallLetter: 1,
23791     kDigit: 2,
23792     kPunctuation: 3,
23793     
23794     insecure: false,
23795     // private
23796     initEvents: function ()
23797     {
23798         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23799
23800         if (this.el.is('input[type=password]') && Roo.isSafari) {
23801             this.el.on('keydown', this.SafariOnKeyDown, this);
23802         }
23803
23804         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23805     },
23806     // private
23807     onRender: function (ct, position)
23808     {
23809         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23810         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23811         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23812
23813         this.trigger.createChild({
23814                    cn: [
23815                     {
23816                     //id: 'PwdMeter',
23817                     tag: 'div',
23818                     cls: 'roo-password-meter-grey col-xs-12',
23819                     style: {
23820                         //width: 0,
23821                         //width: this.meterWidth + 'px'                                                
23822                         }
23823                     },
23824                     {                            
23825                          cls: 'roo-password-meter-text'                          
23826                     }
23827                 ]            
23828         });
23829
23830          
23831         if (this.hideTrigger) {
23832             this.trigger.setDisplayed(false);
23833         }
23834         this.setSize(this.width || '', this.height || '');
23835     },
23836     // private
23837     onDestroy: function ()
23838     {
23839         if (this.trigger) {
23840             this.trigger.removeAllListeners();
23841             this.trigger.remove();
23842         }
23843         if (this.wrap) {
23844             this.wrap.remove();
23845         }
23846         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23847     },
23848     // private
23849     checkStrength: function ()
23850     {
23851         var pwd = this.inputEl().getValue();
23852         if (pwd == this._lastPwd) {
23853             return;
23854         }
23855
23856         var strength;
23857         if (this.ClientSideStrongPassword(pwd)) {
23858             strength = 3;
23859         } else if (this.ClientSideMediumPassword(pwd)) {
23860             strength = 2;
23861         } else if (this.ClientSideWeakPassword(pwd)) {
23862             strength = 1;
23863         } else {
23864             strength = 0;
23865         }
23866         
23867         Roo.log('strength1: ' + strength);
23868         
23869         //var pm = this.trigger.child('div/div/div').dom;
23870         var pm = this.trigger.child('div/div');
23871         pm.removeClass(this.meterClass);
23872         pm.addClass(this.meterClass[strength]);
23873                 
23874         
23875         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23876                 
23877         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23878         
23879         this._lastPwd = pwd;
23880     },
23881     reset: function ()
23882     {
23883         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23884         
23885         this._lastPwd = '';
23886         
23887         var pm = this.trigger.child('div/div');
23888         pm.removeClass(this.meterClass);
23889         pm.addClass('roo-password-meter-grey');        
23890         
23891         
23892         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23893         
23894         pt.innerHTML = '';
23895         this.inputEl().dom.type='password';
23896     },
23897     // private
23898     validateValue: function (value)
23899     {
23900         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23901             return false;
23902         }
23903         if (value.length == 0) {
23904             if (this.allowBlank) {
23905                 this.clearInvalid();
23906                 return true;
23907             }
23908
23909             this.markInvalid(this.errors.PwdEmpty);
23910             this.errorMsg = this.errors.PwdEmpty;
23911             return false;
23912         }
23913         
23914         if(this.insecure){
23915             return true;
23916         }
23917         
23918         if (!value.match(/[\x21-\x7e]+/)) {
23919             this.markInvalid(this.errors.PwdBadChar);
23920             this.errorMsg = this.errors.PwdBadChar;
23921             return false;
23922         }
23923         if (value.length < 6) {
23924             this.markInvalid(this.errors.PwdShort);
23925             this.errorMsg = this.errors.PwdShort;
23926             return false;
23927         }
23928         if (value.length > 16) {
23929             this.markInvalid(this.errors.PwdLong);
23930             this.errorMsg = this.errors.PwdLong;
23931             return false;
23932         }
23933         var strength;
23934         if (this.ClientSideStrongPassword(value)) {
23935             strength = 3;
23936         } else if (this.ClientSideMediumPassword(value)) {
23937             strength = 2;
23938         } else if (this.ClientSideWeakPassword(value)) {
23939             strength = 1;
23940         } else {
23941             strength = 0;
23942         }
23943
23944         
23945         if (strength < 2) {
23946             //this.markInvalid(this.errors.TooWeak);
23947             this.errorMsg = this.errors.TooWeak;
23948             //return false;
23949         }
23950         
23951         
23952         console.log('strength2: ' + strength);
23953         
23954         //var pm = this.trigger.child('div/div/div').dom;
23955         
23956         var pm = this.trigger.child('div/div');
23957         pm.removeClass(this.meterClass);
23958         pm.addClass(this.meterClass[strength]);
23959                 
23960         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23961                 
23962         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23963         
23964         this.errorMsg = ''; 
23965         return true;
23966     },
23967     // private
23968     CharacterSetChecks: function (type)
23969     {
23970         this.type = type;
23971         this.fResult = false;
23972     },
23973     // private
23974     isctype: function (character, type)
23975     {
23976         switch (type) {  
23977             case this.kCapitalLetter:
23978                 if (character >= 'A' && character <= 'Z') {
23979                     return true;
23980                 }
23981                 break;
23982             
23983             case this.kSmallLetter:
23984                 if (character >= 'a' && character <= 'z') {
23985                     return true;
23986                 }
23987                 break;
23988             
23989             case this.kDigit:
23990                 if (character >= '0' && character <= '9') {
23991                     return true;
23992                 }
23993                 break;
23994             
23995             case this.kPunctuation:
23996                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23997                     return true;
23998                 }
23999                 break;
24000             
24001             default:
24002                 return false;
24003         }
24004
24005     },
24006     // private
24007     IsLongEnough: function (pwd, size)
24008     {
24009         return !(pwd == null || isNaN(size) || pwd.length < size);
24010     },
24011     // private
24012     SpansEnoughCharacterSets: function (word, nb)
24013     {
24014         if (!this.IsLongEnough(word, nb))
24015         {
24016             return false;
24017         }
24018
24019         var characterSetChecks = new Array(
24020             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24021             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24022         );
24023         
24024         for (var index = 0; index < word.length; ++index) {
24025             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24026                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24027                     characterSetChecks[nCharSet].fResult = true;
24028                     break;
24029                 }
24030             }
24031         }
24032
24033         var nCharSets = 0;
24034         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24035             if (characterSetChecks[nCharSet].fResult) {
24036                 ++nCharSets;
24037             }
24038         }
24039
24040         if (nCharSets < nb) {
24041             return false;
24042         }
24043         return true;
24044     },
24045     // private
24046     ClientSideStrongPassword: function (pwd)
24047     {
24048         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24049     },
24050     // private
24051     ClientSideMediumPassword: function (pwd)
24052     {
24053         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24054     },
24055     // private
24056     ClientSideWeakPassword: function (pwd)
24057     {
24058         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24059     }
24060           
24061 })//<script type="text/javascript">
24062
24063 /*
24064  * Based  Ext JS Library 1.1.1
24065  * Copyright(c) 2006-2007, Ext JS, LLC.
24066  * LGPL
24067  *
24068  */
24069  
24070 /**
24071  * @class Roo.HtmlEditorCore
24072  * @extends Roo.Component
24073  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24074  *
24075  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24076  */
24077
24078 Roo.HtmlEditorCore = function(config){
24079     
24080     
24081     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24082     
24083     
24084     this.addEvents({
24085         /**
24086          * @event initialize
24087          * Fires when the editor is fully initialized (including the iframe)
24088          * @param {Roo.HtmlEditorCore} this
24089          */
24090         initialize: true,
24091         /**
24092          * @event activate
24093          * Fires when the editor is first receives the focus. Any insertion must wait
24094          * until after this event.
24095          * @param {Roo.HtmlEditorCore} this
24096          */
24097         activate: true,
24098          /**
24099          * @event beforesync
24100          * Fires before the textarea is updated with content from the editor iframe. Return false
24101          * to cancel the sync.
24102          * @param {Roo.HtmlEditorCore} this
24103          * @param {String} html
24104          */
24105         beforesync: true,
24106          /**
24107          * @event beforepush
24108          * Fires before the iframe editor is updated with content from the textarea. Return false
24109          * to cancel the push.
24110          * @param {Roo.HtmlEditorCore} this
24111          * @param {String} html
24112          */
24113         beforepush: true,
24114          /**
24115          * @event sync
24116          * Fires when the textarea is updated with content from the editor iframe.
24117          * @param {Roo.HtmlEditorCore} this
24118          * @param {String} html
24119          */
24120         sync: true,
24121          /**
24122          * @event push
24123          * Fires when the iframe editor is updated with content from the textarea.
24124          * @param {Roo.HtmlEditorCore} this
24125          * @param {String} html
24126          */
24127         push: true,
24128         
24129         /**
24130          * @event editorevent
24131          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24132          * @param {Roo.HtmlEditorCore} this
24133          */
24134         editorevent: true
24135         
24136     });
24137     
24138     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24139     
24140     // defaults : white / black...
24141     this.applyBlacklists();
24142     
24143     
24144     
24145 };
24146
24147
24148 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24149
24150
24151      /**
24152      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24153      */
24154     
24155     owner : false,
24156     
24157      /**
24158      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24159      *                        Roo.resizable.
24160      */
24161     resizable : false,
24162      /**
24163      * @cfg {Number} height (in pixels)
24164      */   
24165     height: 300,
24166    /**
24167      * @cfg {Number} width (in pixels)
24168      */   
24169     width: 500,
24170     
24171     /**
24172      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24173      * 
24174      */
24175     stylesheets: false,
24176     
24177     // id of frame..
24178     frameId: false,
24179     
24180     // private properties
24181     validationEvent : false,
24182     deferHeight: true,
24183     initialized : false,
24184     activated : false,
24185     sourceEditMode : false,
24186     onFocus : Roo.emptyFn,
24187     iframePad:3,
24188     hideMode:'offsets',
24189     
24190     clearUp: true,
24191     
24192     // blacklist + whitelisted elements..
24193     black: false,
24194     white: false,
24195      
24196     bodyCls : '',
24197
24198     /**
24199      * Protected method that will not generally be called directly. It
24200      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24201      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24202      */
24203     getDocMarkup : function(){
24204         // body styles..
24205         var st = '';
24206         
24207         // inherit styels from page...?? 
24208         if (this.stylesheets === false) {
24209             
24210             Roo.get(document.head).select('style').each(function(node) {
24211                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24212             });
24213             
24214             Roo.get(document.head).select('link').each(function(node) { 
24215                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24216             });
24217             
24218         } else if (!this.stylesheets.length) {
24219                 // simple..
24220                 st = '<style type="text/css">' +
24221                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24222                    '</style>';
24223         } else {
24224             for (var i in this.stylesheets) { 
24225                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24226             }
24227             
24228         }
24229         
24230         st +=  '<style type="text/css">' +
24231             'IMG { cursor: pointer } ' +
24232         '</style>';
24233
24234         var cls = 'roo-htmleditor-body';
24235         
24236         if(this.bodyCls.length){
24237             cls += ' ' + this.bodyCls;
24238         }
24239         
24240         return '<html><head>' + st  +
24241             //<style type="text/css">' +
24242             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24243             //'</style>' +
24244             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24245     },
24246
24247     // private
24248     onRender : function(ct, position)
24249     {
24250         var _t = this;
24251         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24252         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24253         
24254         
24255         this.el.dom.style.border = '0 none';
24256         this.el.dom.setAttribute('tabIndex', -1);
24257         this.el.addClass('x-hidden hide');
24258         
24259         
24260         
24261         if(Roo.isIE){ // fix IE 1px bogus margin
24262             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24263         }
24264        
24265         
24266         this.frameId = Roo.id();
24267         
24268          
24269         
24270         var iframe = this.owner.wrap.createChild({
24271             tag: 'iframe',
24272             cls: 'form-control', // bootstrap..
24273             id: this.frameId,
24274             name: this.frameId,
24275             frameBorder : 'no',
24276             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24277         }, this.el
24278         );
24279         
24280         
24281         this.iframe = iframe.dom;
24282
24283          this.assignDocWin();
24284         
24285         this.doc.designMode = 'on';
24286        
24287         this.doc.open();
24288         this.doc.write(this.getDocMarkup());
24289         this.doc.close();
24290
24291         
24292         var task = { // must defer to wait for browser to be ready
24293             run : function(){
24294                 //console.log("run task?" + this.doc.readyState);
24295                 this.assignDocWin();
24296                 if(this.doc.body || this.doc.readyState == 'complete'){
24297                     try {
24298                         this.doc.designMode="on";
24299                     } catch (e) {
24300                         return;
24301                     }
24302                     Roo.TaskMgr.stop(task);
24303                     this.initEditor.defer(10, this);
24304                 }
24305             },
24306             interval : 10,
24307             duration: 10000,
24308             scope: this
24309         };
24310         Roo.TaskMgr.start(task);
24311
24312     },
24313
24314     // private
24315     onResize : function(w, h)
24316     {
24317          Roo.log('resize: ' +w + ',' + h );
24318         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24319         if(!this.iframe){
24320             return;
24321         }
24322         if(typeof w == 'number'){
24323             
24324             this.iframe.style.width = w + 'px';
24325         }
24326         if(typeof h == 'number'){
24327             
24328             this.iframe.style.height = h + 'px';
24329             if(this.doc){
24330                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24331             }
24332         }
24333         
24334     },
24335
24336     /**
24337      * Toggles the editor between standard and source edit mode.
24338      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24339      */
24340     toggleSourceEdit : function(sourceEditMode){
24341         
24342         this.sourceEditMode = sourceEditMode === true;
24343         
24344         if(this.sourceEditMode){
24345  
24346             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24347             
24348         }else{
24349             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24350             //this.iframe.className = '';
24351             this.deferFocus();
24352         }
24353         //this.setSize(this.owner.wrap.getSize());
24354         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24355     },
24356
24357     
24358   
24359
24360     /**
24361      * Protected method that will not generally be called directly. If you need/want
24362      * custom HTML cleanup, this is the method you should override.
24363      * @param {String} html The HTML to be cleaned
24364      * return {String} The cleaned HTML
24365      */
24366     cleanHtml : function(html){
24367         html = String(html);
24368         if(html.length > 5){
24369             if(Roo.isSafari){ // strip safari nonsense
24370                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24371             }
24372         }
24373         if(html == '&nbsp;'){
24374             html = '';
24375         }
24376         return html;
24377     },
24378
24379     /**
24380      * HTML Editor -> Textarea
24381      * Protected method that will not generally be called directly. Syncs the contents
24382      * of the editor iframe with the textarea.
24383      */
24384     syncValue : function(){
24385         if(this.initialized){
24386             var bd = (this.doc.body || this.doc.documentElement);
24387             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24388             var html = bd.innerHTML;
24389             if(Roo.isSafari){
24390                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24391                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24392                 if(m && m[1]){
24393                     html = '<div style="'+m[0]+'">' + html + '</div>';
24394                 }
24395             }
24396             html = this.cleanHtml(html);
24397             // fix up the special chars.. normaly like back quotes in word...
24398             // however we do not want to do this with chinese..
24399             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24400                 
24401                 var cc = match.charCodeAt();
24402
24403                 // Get the character value, handling surrogate pairs
24404                 if (match.length == 2) {
24405                     // It's a surrogate pair, calculate the Unicode code point
24406                     var high = match.charCodeAt(0) - 0xD800;
24407                     var low  = match.charCodeAt(1) - 0xDC00;
24408                     cc = (high * 0x400) + low + 0x10000;
24409                 }  else if (
24410                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24411                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24412                     (cc >= 0xf900 && cc < 0xfb00 )
24413                 ) {
24414                         return match;
24415                 }  
24416          
24417                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24418                 return "&#" + cc + ";";
24419                 
24420                 
24421             });
24422             
24423             
24424              
24425             if(this.owner.fireEvent('beforesync', this, html) !== false){
24426                 this.el.dom.value = html;
24427                 this.owner.fireEvent('sync', this, html);
24428             }
24429         }
24430     },
24431
24432     /**
24433      * Protected method that will not generally be called directly. Pushes the value of the textarea
24434      * into the iframe editor.
24435      */
24436     pushValue : function(){
24437         if(this.initialized){
24438             var v = this.el.dom.value.trim();
24439             
24440 //            if(v.length < 1){
24441 //                v = '&#160;';
24442 //            }
24443             
24444             if(this.owner.fireEvent('beforepush', this, v) !== false){
24445                 var d = (this.doc.body || this.doc.documentElement);
24446                 d.innerHTML = v;
24447                 this.cleanUpPaste();
24448                 this.el.dom.value = d.innerHTML;
24449                 this.owner.fireEvent('push', this, v);
24450             }
24451         }
24452     },
24453
24454     // private
24455     deferFocus : function(){
24456         this.focus.defer(10, this);
24457     },
24458
24459     // doc'ed in Field
24460     focus : function(){
24461         if(this.win && !this.sourceEditMode){
24462             this.win.focus();
24463         }else{
24464             this.el.focus();
24465         }
24466     },
24467     
24468     assignDocWin: function()
24469     {
24470         var iframe = this.iframe;
24471         
24472          if(Roo.isIE){
24473             this.doc = iframe.contentWindow.document;
24474             this.win = iframe.contentWindow;
24475         } else {
24476 //            if (!Roo.get(this.frameId)) {
24477 //                return;
24478 //            }
24479 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24480 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24481             
24482             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24483                 return;
24484             }
24485             
24486             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24487             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24488         }
24489     },
24490     
24491     // private
24492     initEditor : function(){
24493         //console.log("INIT EDITOR");
24494         this.assignDocWin();
24495         
24496         
24497         
24498         this.doc.designMode="on";
24499         this.doc.open();
24500         this.doc.write(this.getDocMarkup());
24501         this.doc.close();
24502         
24503         var dbody = (this.doc.body || this.doc.documentElement);
24504         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24505         // this copies styles from the containing element into thsi one..
24506         // not sure why we need all of this..
24507         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24508         
24509         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24510         //ss['background-attachment'] = 'fixed'; // w3c
24511         dbody.bgProperties = 'fixed'; // ie
24512         //Roo.DomHelper.applyStyles(dbody, ss);
24513         Roo.EventManager.on(this.doc, {
24514             //'mousedown': this.onEditorEvent,
24515             'mouseup': this.onEditorEvent,
24516             'dblclick': this.onEditorEvent,
24517             'click': this.onEditorEvent,
24518             'keyup': this.onEditorEvent,
24519             buffer:100,
24520             scope: this
24521         });
24522         if(Roo.isGecko){
24523             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24524         }
24525         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24526             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24527         }
24528         this.initialized = true;
24529
24530         this.owner.fireEvent('initialize', this);
24531         this.pushValue();
24532     },
24533
24534     // private
24535     onDestroy : function(){
24536         
24537         
24538         
24539         if(this.rendered){
24540             
24541             //for (var i =0; i < this.toolbars.length;i++) {
24542             //    // fixme - ask toolbars for heights?
24543             //    this.toolbars[i].onDestroy();
24544            // }
24545             
24546             //this.wrap.dom.innerHTML = '';
24547             //this.wrap.remove();
24548         }
24549     },
24550
24551     // private
24552     onFirstFocus : function(){
24553         
24554         this.assignDocWin();
24555         
24556         
24557         this.activated = true;
24558          
24559     
24560         if(Roo.isGecko){ // prevent silly gecko errors
24561             this.win.focus();
24562             var s = this.win.getSelection();
24563             if(!s.focusNode || s.focusNode.nodeType != 3){
24564                 var r = s.getRangeAt(0);
24565                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24566                 r.collapse(true);
24567                 this.deferFocus();
24568             }
24569             try{
24570                 this.execCmd('useCSS', true);
24571                 this.execCmd('styleWithCSS', false);
24572             }catch(e){}
24573         }
24574         this.owner.fireEvent('activate', this);
24575     },
24576
24577     // private
24578     adjustFont: function(btn){
24579         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24580         //if(Roo.isSafari){ // safari
24581         //    adjust *= 2;
24582        // }
24583         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24584         if(Roo.isSafari){ // safari
24585             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24586             v =  (v < 10) ? 10 : v;
24587             v =  (v > 48) ? 48 : v;
24588             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24589             
24590         }
24591         
24592         
24593         v = Math.max(1, v+adjust);
24594         
24595         this.execCmd('FontSize', v  );
24596     },
24597
24598     onEditorEvent : function(e)
24599     {
24600         this.owner.fireEvent('editorevent', this, e);
24601       //  this.updateToolbar();
24602         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24603     },
24604
24605     insertTag : function(tg)
24606     {
24607         // could be a bit smarter... -> wrap the current selected tRoo..
24608         if (tg.toLowerCase() == 'span' ||
24609             tg.toLowerCase() == 'code' ||
24610             tg.toLowerCase() == 'sup' ||
24611             tg.toLowerCase() == 'sub' 
24612             ) {
24613             
24614             range = this.createRange(this.getSelection());
24615             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24616             wrappingNode.appendChild(range.extractContents());
24617             range.insertNode(wrappingNode);
24618
24619             return;
24620             
24621             
24622             
24623         }
24624         this.execCmd("formatblock",   tg);
24625         
24626     },
24627     
24628     insertText : function(txt)
24629     {
24630         
24631         
24632         var range = this.createRange();
24633         range.deleteContents();
24634                //alert(Sender.getAttribute('label'));
24635                
24636         range.insertNode(this.doc.createTextNode(txt));
24637     } ,
24638     
24639      
24640
24641     /**
24642      * Executes a Midas editor command on the editor document and performs necessary focus and
24643      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24644      * @param {String} cmd The Midas command
24645      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24646      */
24647     relayCmd : function(cmd, value){
24648         this.win.focus();
24649         this.execCmd(cmd, value);
24650         this.owner.fireEvent('editorevent', this);
24651         //this.updateToolbar();
24652         this.owner.deferFocus();
24653     },
24654
24655     /**
24656      * Executes a Midas editor command directly on the editor document.
24657      * For visual commands, you should use {@link #relayCmd} instead.
24658      * <b>This should only be called after the editor is initialized.</b>
24659      * @param {String} cmd The Midas command
24660      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24661      */
24662     execCmd : function(cmd, value){
24663         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24664         this.syncValue();
24665     },
24666  
24667  
24668    
24669     /**
24670      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24671      * to insert tRoo.
24672      * @param {String} text | dom node.. 
24673      */
24674     insertAtCursor : function(text)
24675     {
24676         
24677         if(!this.activated){
24678             return;
24679         }
24680         /*
24681         if(Roo.isIE){
24682             this.win.focus();
24683             var r = this.doc.selection.createRange();
24684             if(r){
24685                 r.collapse(true);
24686                 r.pasteHTML(text);
24687                 this.syncValue();
24688                 this.deferFocus();
24689             
24690             }
24691             return;
24692         }
24693         */
24694         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24695             this.win.focus();
24696             
24697             
24698             // from jquery ui (MIT licenced)
24699             var range, node;
24700             var win = this.win;
24701             
24702             if (win.getSelection && win.getSelection().getRangeAt) {
24703                 range = win.getSelection().getRangeAt(0);
24704                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24705                 range.insertNode(node);
24706             } else if (win.document.selection && win.document.selection.createRange) {
24707                 // no firefox support
24708                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24709                 win.document.selection.createRange().pasteHTML(txt);
24710             } else {
24711                 // no firefox support
24712                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24713                 this.execCmd('InsertHTML', txt);
24714             } 
24715             
24716             this.syncValue();
24717             
24718             this.deferFocus();
24719         }
24720     },
24721  // private
24722     mozKeyPress : function(e){
24723         if(e.ctrlKey){
24724             var c = e.getCharCode(), cmd;
24725           
24726             if(c > 0){
24727                 c = String.fromCharCode(c).toLowerCase();
24728                 switch(c){
24729                     case 'b':
24730                         cmd = 'bold';
24731                         break;
24732                     case 'i':
24733                         cmd = 'italic';
24734                         break;
24735                     
24736                     case 'u':
24737                         cmd = 'underline';
24738                         break;
24739                     
24740                     case 'v':
24741                         this.cleanUpPaste.defer(100, this);
24742                         return;
24743                         
24744                 }
24745                 if(cmd){
24746                     this.win.focus();
24747                     this.execCmd(cmd);
24748                     this.deferFocus();
24749                     e.preventDefault();
24750                 }
24751                 
24752             }
24753         }
24754     },
24755
24756     // private
24757     fixKeys : function(){ // load time branching for fastest keydown performance
24758         if(Roo.isIE){
24759             return function(e){
24760                 var k = e.getKey(), r;
24761                 if(k == e.TAB){
24762                     e.stopEvent();
24763                     r = this.doc.selection.createRange();
24764                     if(r){
24765                         r.collapse(true);
24766                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24767                         this.deferFocus();
24768                     }
24769                     return;
24770                 }
24771                 
24772                 if(k == e.ENTER){
24773                     r = this.doc.selection.createRange();
24774                     if(r){
24775                         var target = r.parentElement();
24776                         if(!target || target.tagName.toLowerCase() != 'li'){
24777                             e.stopEvent();
24778                             r.pasteHTML('<br />');
24779                             r.collapse(false);
24780                             r.select();
24781                         }
24782                     }
24783                 }
24784                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24785                     this.cleanUpPaste.defer(100, this);
24786                     return;
24787                 }
24788                 
24789                 
24790             };
24791         }else if(Roo.isOpera){
24792             return function(e){
24793                 var k = e.getKey();
24794                 if(k == e.TAB){
24795                     e.stopEvent();
24796                     this.win.focus();
24797                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24798                     this.deferFocus();
24799                 }
24800                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24801                     this.cleanUpPaste.defer(100, this);
24802                     return;
24803                 }
24804                 
24805             };
24806         }else if(Roo.isSafari){
24807             return function(e){
24808                 var k = e.getKey();
24809                 
24810                 if(k == e.TAB){
24811                     e.stopEvent();
24812                     this.execCmd('InsertText','\t');
24813                     this.deferFocus();
24814                     return;
24815                 }
24816                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24817                     this.cleanUpPaste.defer(100, this);
24818                     return;
24819                 }
24820                 
24821              };
24822         }
24823     }(),
24824     
24825     getAllAncestors: function()
24826     {
24827         var p = this.getSelectedNode();
24828         var a = [];
24829         if (!p) {
24830             a.push(p); // push blank onto stack..
24831             p = this.getParentElement();
24832         }
24833         
24834         
24835         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24836             a.push(p);
24837             p = p.parentNode;
24838         }
24839         a.push(this.doc.body);
24840         return a;
24841     },
24842     lastSel : false,
24843     lastSelNode : false,
24844     
24845     
24846     getSelection : function() 
24847     {
24848         this.assignDocWin();
24849         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24850     },
24851     
24852     getSelectedNode: function() 
24853     {
24854         // this may only work on Gecko!!!
24855         
24856         // should we cache this!!!!
24857         
24858         
24859         
24860          
24861         var range = this.createRange(this.getSelection()).cloneRange();
24862         
24863         if (Roo.isIE) {
24864             var parent = range.parentElement();
24865             while (true) {
24866                 var testRange = range.duplicate();
24867                 testRange.moveToElementText(parent);
24868                 if (testRange.inRange(range)) {
24869                     break;
24870                 }
24871                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24872                     break;
24873                 }
24874                 parent = parent.parentElement;
24875             }
24876             return parent;
24877         }
24878         
24879         // is ancestor a text element.
24880         var ac =  range.commonAncestorContainer;
24881         if (ac.nodeType == 3) {
24882             ac = ac.parentNode;
24883         }
24884         
24885         var ar = ac.childNodes;
24886          
24887         var nodes = [];
24888         var other_nodes = [];
24889         var has_other_nodes = false;
24890         for (var i=0;i<ar.length;i++) {
24891             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24892                 continue;
24893             }
24894             // fullly contained node.
24895             
24896             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24897                 nodes.push(ar[i]);
24898                 continue;
24899             }
24900             
24901             // probably selected..
24902             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24903                 other_nodes.push(ar[i]);
24904                 continue;
24905             }
24906             // outer..
24907             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24908                 continue;
24909             }
24910             
24911             
24912             has_other_nodes = true;
24913         }
24914         if (!nodes.length && other_nodes.length) {
24915             nodes= other_nodes;
24916         }
24917         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24918             return false;
24919         }
24920         
24921         return nodes[0];
24922     },
24923     createRange: function(sel)
24924     {
24925         // this has strange effects when using with 
24926         // top toolbar - not sure if it's a great idea.
24927         //this.editor.contentWindow.focus();
24928         if (typeof sel != "undefined") {
24929             try {
24930                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24931             } catch(e) {
24932                 return this.doc.createRange();
24933             }
24934         } else {
24935             return this.doc.createRange();
24936         }
24937     },
24938     getParentElement: function()
24939     {
24940         
24941         this.assignDocWin();
24942         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24943         
24944         var range = this.createRange(sel);
24945          
24946         try {
24947             var p = range.commonAncestorContainer;
24948             while (p.nodeType == 3) { // text node
24949                 p = p.parentNode;
24950             }
24951             return p;
24952         } catch (e) {
24953             return null;
24954         }
24955     
24956     },
24957     /***
24958      *
24959      * Range intersection.. the hard stuff...
24960      *  '-1' = before
24961      *  '0' = hits..
24962      *  '1' = after.
24963      *         [ -- selected range --- ]
24964      *   [fail]                        [fail]
24965      *
24966      *    basically..
24967      *      if end is before start or  hits it. fail.
24968      *      if start is after end or hits it fail.
24969      *
24970      *   if either hits (but other is outside. - then it's not 
24971      *   
24972      *    
24973      **/
24974     
24975     
24976     // @see http://www.thismuchiknow.co.uk/?p=64.
24977     rangeIntersectsNode : function(range, node)
24978     {
24979         var nodeRange = node.ownerDocument.createRange();
24980         try {
24981             nodeRange.selectNode(node);
24982         } catch (e) {
24983             nodeRange.selectNodeContents(node);
24984         }
24985     
24986         var rangeStartRange = range.cloneRange();
24987         rangeStartRange.collapse(true);
24988     
24989         var rangeEndRange = range.cloneRange();
24990         rangeEndRange.collapse(false);
24991     
24992         var nodeStartRange = nodeRange.cloneRange();
24993         nodeStartRange.collapse(true);
24994     
24995         var nodeEndRange = nodeRange.cloneRange();
24996         nodeEndRange.collapse(false);
24997     
24998         return rangeStartRange.compareBoundaryPoints(
24999                  Range.START_TO_START, nodeEndRange) == -1 &&
25000                rangeEndRange.compareBoundaryPoints(
25001                  Range.START_TO_START, nodeStartRange) == 1;
25002         
25003          
25004     },
25005     rangeCompareNode : function(range, node)
25006     {
25007         var nodeRange = node.ownerDocument.createRange();
25008         try {
25009             nodeRange.selectNode(node);
25010         } catch (e) {
25011             nodeRange.selectNodeContents(node);
25012         }
25013         
25014         
25015         range.collapse(true);
25016     
25017         nodeRange.collapse(true);
25018      
25019         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25020         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25021          
25022         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25023         
25024         var nodeIsBefore   =  ss == 1;
25025         var nodeIsAfter    = ee == -1;
25026         
25027         if (nodeIsBefore && nodeIsAfter) {
25028             return 0; // outer
25029         }
25030         if (!nodeIsBefore && nodeIsAfter) {
25031             return 1; //right trailed.
25032         }
25033         
25034         if (nodeIsBefore && !nodeIsAfter) {
25035             return 2;  // left trailed.
25036         }
25037         // fully contined.
25038         return 3;
25039     },
25040
25041     // private? - in a new class?
25042     cleanUpPaste :  function()
25043     {
25044         // cleans up the whole document..
25045         Roo.log('cleanuppaste');
25046         
25047         this.cleanUpChildren(this.doc.body);
25048         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25049         if (clean != this.doc.body.innerHTML) {
25050             this.doc.body.innerHTML = clean;
25051         }
25052         
25053     },
25054     
25055     cleanWordChars : function(input) {// change the chars to hex code
25056         var he = Roo.HtmlEditorCore;
25057         
25058         var output = input;
25059         Roo.each(he.swapCodes, function(sw) { 
25060             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25061             
25062             output = output.replace(swapper, sw[1]);
25063         });
25064         
25065         return output;
25066     },
25067     
25068     
25069     cleanUpChildren : function (n)
25070     {
25071         if (!n.childNodes.length) {
25072             return;
25073         }
25074         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25075            this.cleanUpChild(n.childNodes[i]);
25076         }
25077     },
25078     
25079     
25080         
25081     
25082     cleanUpChild : function (node)
25083     {
25084         var ed = this;
25085         //console.log(node);
25086         if (node.nodeName == "#text") {
25087             // clean up silly Windows -- stuff?
25088             return; 
25089         }
25090         if (node.nodeName == "#comment") {
25091             node.parentNode.removeChild(node);
25092             // clean up silly Windows -- stuff?
25093             return; 
25094         }
25095         var lcname = node.tagName.toLowerCase();
25096         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25097         // whitelist of tags..
25098         
25099         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25100             // remove node.
25101             node.parentNode.removeChild(node);
25102             return;
25103             
25104         }
25105         
25106         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25107         
25108         // spans with no attributes - just remove them..
25109         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
25110             remove_keep_children = true;
25111         }
25112         
25113         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25114         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25115         
25116         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25117         //    remove_keep_children = true;
25118         //}
25119         
25120         if (remove_keep_children) {
25121             this.cleanUpChildren(node);
25122             // inserts everything just before this node...
25123             while (node.childNodes.length) {
25124                 var cn = node.childNodes[0];
25125                 node.removeChild(cn);
25126                 node.parentNode.insertBefore(cn, node);
25127             }
25128             node.parentNode.removeChild(node);
25129             return;
25130         }
25131         
25132         if (!node.attributes || !node.attributes.length) {
25133             
25134           
25135             
25136             
25137             this.cleanUpChildren(node);
25138             return;
25139         }
25140         
25141         function cleanAttr(n,v)
25142         {
25143             
25144             if (v.match(/^\./) || v.match(/^\//)) {
25145                 return;
25146             }
25147             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25148                 return;
25149             }
25150             if (v.match(/^#/)) {
25151                 return;
25152             }
25153             if (v.match(/^\{/)) { // allow template editing.
25154                 return;
25155             }
25156 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25157             node.removeAttribute(n);
25158             
25159         }
25160         
25161         var cwhite = this.cwhite;
25162         var cblack = this.cblack;
25163             
25164         function cleanStyle(n,v)
25165         {
25166             if (v.match(/expression/)) { //XSS?? should we even bother..
25167                 node.removeAttribute(n);
25168                 return;
25169             }
25170             
25171             var parts = v.split(/;/);
25172             var clean = [];
25173             
25174             Roo.each(parts, function(p) {
25175                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25176                 if (!p.length) {
25177                     return true;
25178                 }
25179                 var l = p.split(':').shift().replace(/\s+/g,'');
25180                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25181                 
25182                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25183 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25184                     //node.removeAttribute(n);
25185                     return true;
25186                 }
25187                 //Roo.log()
25188                 // only allow 'c whitelisted system attributes'
25189                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25190 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25191                     //node.removeAttribute(n);
25192                     return true;
25193                 }
25194                 
25195                 
25196                  
25197                 
25198                 clean.push(p);
25199                 return true;
25200             });
25201             if (clean.length) { 
25202                 node.setAttribute(n, clean.join(';'));
25203             } else {
25204                 node.removeAttribute(n);
25205             }
25206             
25207         }
25208         
25209         
25210         for (var i = node.attributes.length-1; i > -1 ; i--) {
25211             var a = node.attributes[i];
25212             //console.log(a);
25213             
25214             if (a.name.toLowerCase().substr(0,2)=='on')  {
25215                 node.removeAttribute(a.name);
25216                 continue;
25217             }
25218             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25219                 node.removeAttribute(a.name);
25220                 continue;
25221             }
25222             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25223                 cleanAttr(a.name,a.value); // fixme..
25224                 continue;
25225             }
25226             if (a.name == 'style') {
25227                 cleanStyle(a.name,a.value);
25228                 continue;
25229             }
25230             /// clean up MS crap..
25231             // tecnically this should be a list of valid class'es..
25232             
25233             
25234             if (a.name == 'class') {
25235                 if (a.value.match(/^Mso/)) {
25236                     node.removeAttribute('class');
25237                 }
25238                 
25239                 if (a.value.match(/^body$/)) {
25240                     node.removeAttribute('class');
25241                 }
25242                 continue;
25243             }
25244             
25245             // style cleanup!?
25246             // class cleanup?
25247             
25248         }
25249         
25250         
25251         this.cleanUpChildren(node);
25252         
25253         
25254     },
25255     
25256     /**
25257      * Clean up MS wordisms...
25258      */
25259     cleanWord : function(node)
25260     {
25261         if (!node) {
25262             this.cleanWord(this.doc.body);
25263             return;
25264         }
25265         
25266         if(
25267                 node.nodeName == 'SPAN' &&
25268                 !node.hasAttributes() &&
25269                 node.childNodes.length == 1 &&
25270                 node.firstChild.nodeName == "#text"  
25271         ) {
25272             var textNode = node.firstChild;
25273             node.removeChild(textNode);
25274             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25275                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25276             }
25277             node.parentNode.insertBefore(textNode, node);
25278             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25279                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25280             }
25281             node.parentNode.removeChild(node);
25282         }
25283         
25284         if (node.nodeName == "#text") {
25285             // clean up silly Windows -- stuff?
25286             return; 
25287         }
25288         if (node.nodeName == "#comment") {
25289             node.parentNode.removeChild(node);
25290             // clean up silly Windows -- stuff?
25291             return; 
25292         }
25293         
25294         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25295             node.parentNode.removeChild(node);
25296             return;
25297         }
25298         //Roo.log(node.tagName);
25299         // remove - but keep children..
25300         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25301             //Roo.log('-- removed');
25302             while (node.childNodes.length) {
25303                 var cn = node.childNodes[0];
25304                 node.removeChild(cn);
25305                 node.parentNode.insertBefore(cn, node);
25306                 // move node to parent - and clean it..
25307                 this.cleanWord(cn);
25308             }
25309             node.parentNode.removeChild(node);
25310             /// no need to iterate chidlren = it's got none..
25311             //this.iterateChildren(node, this.cleanWord);
25312             return;
25313         }
25314         // clean styles
25315         if (node.className.length) {
25316             
25317             var cn = node.className.split(/\W+/);
25318             var cna = [];
25319             Roo.each(cn, function(cls) {
25320                 if (cls.match(/Mso[a-zA-Z]+/)) {
25321                     return;
25322                 }
25323                 cna.push(cls);
25324             });
25325             node.className = cna.length ? cna.join(' ') : '';
25326             if (!cna.length) {
25327                 node.removeAttribute("class");
25328             }
25329         }
25330         
25331         if (node.hasAttribute("lang")) {
25332             node.removeAttribute("lang");
25333         }
25334         
25335         if (node.hasAttribute("style")) {
25336             
25337             var styles = node.getAttribute("style").split(";");
25338             var nstyle = [];
25339             Roo.each(styles, function(s) {
25340                 if (!s.match(/:/)) {
25341                     return;
25342                 }
25343                 var kv = s.split(":");
25344                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25345                     return;
25346                 }
25347                 // what ever is left... we allow.
25348                 nstyle.push(s);
25349             });
25350             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25351             if (!nstyle.length) {
25352                 node.removeAttribute('style');
25353             }
25354         }
25355         this.iterateChildren(node, this.cleanWord);
25356         
25357         
25358         
25359     },
25360     /**
25361      * iterateChildren of a Node, calling fn each time, using this as the scole..
25362      * @param {DomNode} node node to iterate children of.
25363      * @param {Function} fn method of this class to call on each item.
25364      */
25365     iterateChildren : function(node, fn)
25366     {
25367         if (!node.childNodes.length) {
25368                 return;
25369         }
25370         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25371            fn.call(this, node.childNodes[i])
25372         }
25373     },
25374     
25375     
25376     /**
25377      * cleanTableWidths.
25378      *
25379      * Quite often pasting from word etc.. results in tables with column and widths.
25380      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25381      *
25382      */
25383     cleanTableWidths : function(node)
25384     {
25385          
25386          
25387         if (!node) {
25388             this.cleanTableWidths(this.doc.body);
25389             return;
25390         }
25391         
25392         // ignore list...
25393         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25394             return; 
25395         }
25396         Roo.log(node.tagName);
25397         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25398             this.iterateChildren(node, this.cleanTableWidths);
25399             return;
25400         }
25401         if (node.hasAttribute('width')) {
25402             node.removeAttribute('width');
25403         }
25404         
25405          
25406         if (node.hasAttribute("style")) {
25407             // pretty basic...
25408             
25409             var styles = node.getAttribute("style").split(";");
25410             var nstyle = [];
25411             Roo.each(styles, function(s) {
25412                 if (!s.match(/:/)) {
25413                     return;
25414                 }
25415                 var kv = s.split(":");
25416                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25417                     return;
25418                 }
25419                 // what ever is left... we allow.
25420                 nstyle.push(s);
25421             });
25422             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25423             if (!nstyle.length) {
25424                 node.removeAttribute('style');
25425             }
25426         }
25427         
25428         this.iterateChildren(node, this.cleanTableWidths);
25429         
25430         
25431     },
25432     
25433     
25434     
25435     
25436     domToHTML : function(currentElement, depth, nopadtext) {
25437         
25438         depth = depth || 0;
25439         nopadtext = nopadtext || false;
25440     
25441         if (!currentElement) {
25442             return this.domToHTML(this.doc.body);
25443         }
25444         
25445         //Roo.log(currentElement);
25446         var j;
25447         var allText = false;
25448         var nodeName = currentElement.nodeName;
25449         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25450         
25451         if  (nodeName == '#text') {
25452             
25453             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25454         }
25455         
25456         
25457         var ret = '';
25458         if (nodeName != 'BODY') {
25459              
25460             var i = 0;
25461             // Prints the node tagName, such as <A>, <IMG>, etc
25462             if (tagName) {
25463                 var attr = [];
25464                 for(i = 0; i < currentElement.attributes.length;i++) {
25465                     // quoting?
25466                     var aname = currentElement.attributes.item(i).name;
25467                     if (!currentElement.attributes.item(i).value.length) {
25468                         continue;
25469                     }
25470                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25471                 }
25472                 
25473                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25474             } 
25475             else {
25476                 
25477                 // eack
25478             }
25479         } else {
25480             tagName = false;
25481         }
25482         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25483             return ret;
25484         }
25485         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25486             nopadtext = true;
25487         }
25488         
25489         
25490         // Traverse the tree
25491         i = 0;
25492         var currentElementChild = currentElement.childNodes.item(i);
25493         var allText = true;
25494         var innerHTML  = '';
25495         lastnode = '';
25496         while (currentElementChild) {
25497             // Formatting code (indent the tree so it looks nice on the screen)
25498             var nopad = nopadtext;
25499             if (lastnode == 'SPAN') {
25500                 nopad  = true;
25501             }
25502             // text
25503             if  (currentElementChild.nodeName == '#text') {
25504                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25505                 toadd = nopadtext ? toadd : toadd.trim();
25506                 if (!nopad && toadd.length > 80) {
25507                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25508                 }
25509                 innerHTML  += toadd;
25510                 
25511                 i++;
25512                 currentElementChild = currentElement.childNodes.item(i);
25513                 lastNode = '';
25514                 continue;
25515             }
25516             allText = false;
25517             
25518             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25519                 
25520             // Recursively traverse the tree structure of the child node
25521             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25522             lastnode = currentElementChild.nodeName;
25523             i++;
25524             currentElementChild=currentElement.childNodes.item(i);
25525         }
25526         
25527         ret += innerHTML;
25528         
25529         if (!allText) {
25530                 // The remaining code is mostly for formatting the tree
25531             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25532         }
25533         
25534         
25535         if (tagName) {
25536             ret+= "</"+tagName+">";
25537         }
25538         return ret;
25539         
25540     },
25541         
25542     applyBlacklists : function()
25543     {
25544         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25545         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25546         
25547         this.white = [];
25548         this.black = [];
25549         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25550             if (b.indexOf(tag) > -1) {
25551                 return;
25552             }
25553             this.white.push(tag);
25554             
25555         }, this);
25556         
25557         Roo.each(w, function(tag) {
25558             if (b.indexOf(tag) > -1) {
25559                 return;
25560             }
25561             if (this.white.indexOf(tag) > -1) {
25562                 return;
25563             }
25564             this.white.push(tag);
25565             
25566         }, this);
25567         
25568         
25569         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25570             if (w.indexOf(tag) > -1) {
25571                 return;
25572             }
25573             this.black.push(tag);
25574             
25575         }, this);
25576         
25577         Roo.each(b, function(tag) {
25578             if (w.indexOf(tag) > -1) {
25579                 return;
25580             }
25581             if (this.black.indexOf(tag) > -1) {
25582                 return;
25583             }
25584             this.black.push(tag);
25585             
25586         }, this);
25587         
25588         
25589         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25590         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25591         
25592         this.cwhite = [];
25593         this.cblack = [];
25594         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25595             if (b.indexOf(tag) > -1) {
25596                 return;
25597             }
25598             this.cwhite.push(tag);
25599             
25600         }, this);
25601         
25602         Roo.each(w, function(tag) {
25603             if (b.indexOf(tag) > -1) {
25604                 return;
25605             }
25606             if (this.cwhite.indexOf(tag) > -1) {
25607                 return;
25608             }
25609             this.cwhite.push(tag);
25610             
25611         }, this);
25612         
25613         
25614         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25615             if (w.indexOf(tag) > -1) {
25616                 return;
25617             }
25618             this.cblack.push(tag);
25619             
25620         }, this);
25621         
25622         Roo.each(b, function(tag) {
25623             if (w.indexOf(tag) > -1) {
25624                 return;
25625             }
25626             if (this.cblack.indexOf(tag) > -1) {
25627                 return;
25628             }
25629             this.cblack.push(tag);
25630             
25631         }, this);
25632     },
25633     
25634     setStylesheets : function(stylesheets)
25635     {
25636         if(typeof(stylesheets) == 'string'){
25637             Roo.get(this.iframe.contentDocument.head).createChild({
25638                 tag : 'link',
25639                 rel : 'stylesheet',
25640                 type : 'text/css',
25641                 href : stylesheets
25642             });
25643             
25644             return;
25645         }
25646         var _this = this;
25647      
25648         Roo.each(stylesheets, function(s) {
25649             if(!s.length){
25650                 return;
25651             }
25652             
25653             Roo.get(_this.iframe.contentDocument.head).createChild({
25654                 tag : 'link',
25655                 rel : 'stylesheet',
25656                 type : 'text/css',
25657                 href : s
25658             });
25659         });
25660
25661         
25662     },
25663     
25664     removeStylesheets : function()
25665     {
25666         var _this = this;
25667         
25668         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25669             s.remove();
25670         });
25671     },
25672     
25673     setStyle : function(style)
25674     {
25675         Roo.get(this.iframe.contentDocument.head).createChild({
25676             tag : 'style',
25677             type : 'text/css',
25678             html : style
25679         });
25680
25681         return;
25682     }
25683     
25684     // hide stuff that is not compatible
25685     /**
25686      * @event blur
25687      * @hide
25688      */
25689     /**
25690      * @event change
25691      * @hide
25692      */
25693     /**
25694      * @event focus
25695      * @hide
25696      */
25697     /**
25698      * @event specialkey
25699      * @hide
25700      */
25701     /**
25702      * @cfg {String} fieldClass @hide
25703      */
25704     /**
25705      * @cfg {String} focusClass @hide
25706      */
25707     /**
25708      * @cfg {String} autoCreate @hide
25709      */
25710     /**
25711      * @cfg {String} inputType @hide
25712      */
25713     /**
25714      * @cfg {String} invalidClass @hide
25715      */
25716     /**
25717      * @cfg {String} invalidText @hide
25718      */
25719     /**
25720      * @cfg {String} msgFx @hide
25721      */
25722     /**
25723      * @cfg {String} validateOnBlur @hide
25724      */
25725 });
25726
25727 Roo.HtmlEditorCore.white = [
25728         'area', 'br', 'img', 'input', 'hr', 'wbr',
25729         
25730        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25731        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25732        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25733        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25734        'table',   'ul',         'xmp', 
25735        
25736        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25737       'thead',   'tr', 
25738      
25739       'dir', 'menu', 'ol', 'ul', 'dl',
25740        
25741       'embed',  'object'
25742 ];
25743
25744
25745 Roo.HtmlEditorCore.black = [
25746     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25747         'applet', // 
25748         'base',   'basefont', 'bgsound', 'blink',  'body', 
25749         'frame',  'frameset', 'head',    'html',   'ilayer', 
25750         'iframe', 'layer',  'link',     'meta',    'object',   
25751         'script', 'style' ,'title',  'xml' // clean later..
25752 ];
25753 Roo.HtmlEditorCore.clean = [
25754     'script', 'style', 'title', 'xml'
25755 ];
25756 Roo.HtmlEditorCore.remove = [
25757     'font'
25758 ];
25759 // attributes..
25760
25761 Roo.HtmlEditorCore.ablack = [
25762     'on'
25763 ];
25764     
25765 Roo.HtmlEditorCore.aclean = [ 
25766     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25767 ];
25768
25769 // protocols..
25770 Roo.HtmlEditorCore.pwhite= [
25771         'http',  'https',  'mailto'
25772 ];
25773
25774 // white listed style attributes.
25775 Roo.HtmlEditorCore.cwhite= [
25776       //  'text-align', /// default is to allow most things..
25777       
25778          
25779 //        'font-size'//??
25780 ];
25781
25782 // black listed style attributes.
25783 Roo.HtmlEditorCore.cblack= [
25784       //  'font-size' -- this can be set by the project 
25785 ];
25786
25787
25788 Roo.HtmlEditorCore.swapCodes   =[ 
25789     [    8211, "--" ], 
25790     [    8212, "--" ], 
25791     [    8216,  "'" ],  
25792     [    8217, "'" ],  
25793     [    8220, '"' ],  
25794     [    8221, '"' ],  
25795     [    8226, "*" ],  
25796     [    8230, "..." ]
25797 ]; 
25798
25799     /*
25800  * - LGPL
25801  *
25802  * HtmlEditor
25803  * 
25804  */
25805
25806 /**
25807  * @class Roo.bootstrap.HtmlEditor
25808  * @extends Roo.bootstrap.TextArea
25809  * Bootstrap HtmlEditor class
25810
25811  * @constructor
25812  * Create a new HtmlEditor
25813  * @param {Object} config The config object
25814  */
25815
25816 Roo.bootstrap.HtmlEditor = function(config){
25817     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25818     if (!this.toolbars) {
25819         this.toolbars = [];
25820     }
25821     
25822     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25823     this.addEvents({
25824             /**
25825              * @event initialize
25826              * Fires when the editor is fully initialized (including the iframe)
25827              * @param {HtmlEditor} this
25828              */
25829             initialize: true,
25830             /**
25831              * @event activate
25832              * Fires when the editor is first receives the focus. Any insertion must wait
25833              * until after this event.
25834              * @param {HtmlEditor} this
25835              */
25836             activate: true,
25837              /**
25838              * @event beforesync
25839              * Fires before the textarea is updated with content from the editor iframe. Return false
25840              * to cancel the sync.
25841              * @param {HtmlEditor} this
25842              * @param {String} html
25843              */
25844             beforesync: true,
25845              /**
25846              * @event beforepush
25847              * Fires before the iframe editor is updated with content from the textarea. Return false
25848              * to cancel the push.
25849              * @param {HtmlEditor} this
25850              * @param {String} html
25851              */
25852             beforepush: true,
25853              /**
25854              * @event sync
25855              * Fires when the textarea is updated with content from the editor iframe.
25856              * @param {HtmlEditor} this
25857              * @param {String} html
25858              */
25859             sync: true,
25860              /**
25861              * @event push
25862              * Fires when the iframe editor is updated with content from the textarea.
25863              * @param {HtmlEditor} this
25864              * @param {String} html
25865              */
25866             push: true,
25867              /**
25868              * @event editmodechange
25869              * Fires when the editor switches edit modes
25870              * @param {HtmlEditor} this
25871              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25872              */
25873             editmodechange: true,
25874             /**
25875              * @event editorevent
25876              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25877              * @param {HtmlEditor} this
25878              */
25879             editorevent: true,
25880             /**
25881              * @event firstfocus
25882              * Fires when on first focus - needed by toolbars..
25883              * @param {HtmlEditor} this
25884              */
25885             firstfocus: true,
25886             /**
25887              * @event autosave
25888              * Auto save the htmlEditor value as a file into Events
25889              * @param {HtmlEditor} this
25890              */
25891             autosave: true,
25892             /**
25893              * @event savedpreview
25894              * preview the saved version of htmlEditor
25895              * @param {HtmlEditor} this
25896              */
25897             savedpreview: true
25898         });
25899 };
25900
25901
25902 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25903     
25904     
25905       /**
25906      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25907      */
25908     toolbars : false,
25909     
25910      /**
25911     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25912     */
25913     btns : [],
25914    
25915      /**
25916      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25917      *                        Roo.resizable.
25918      */
25919     resizable : false,
25920      /**
25921      * @cfg {Number} height (in pixels)
25922      */   
25923     height: 300,
25924    /**
25925      * @cfg {Number} width (in pixels)
25926      */   
25927     width: false,
25928     
25929     /**
25930      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25931      * 
25932      */
25933     stylesheets: false,
25934     
25935     // id of frame..
25936     frameId: false,
25937     
25938     // private properties
25939     validationEvent : false,
25940     deferHeight: true,
25941     initialized : false,
25942     activated : false,
25943     
25944     onFocus : Roo.emptyFn,
25945     iframePad:3,
25946     hideMode:'offsets',
25947     
25948     tbContainer : false,
25949     
25950     bodyCls : '',
25951     
25952     toolbarContainer :function() {
25953         return this.wrap.select('.x-html-editor-tb',true).first();
25954     },
25955
25956     /**
25957      * Protected method that will not generally be called directly. It
25958      * is called when the editor creates its toolbar. Override this method if you need to
25959      * add custom toolbar buttons.
25960      * @param {HtmlEditor} editor
25961      */
25962     createToolbar : function(){
25963         Roo.log('renewing');
25964         Roo.log("create toolbars");
25965         
25966         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25967         this.toolbars[0].render(this.toolbarContainer());
25968         
25969         return;
25970         
25971 //        if (!editor.toolbars || !editor.toolbars.length) {
25972 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25973 //        }
25974 //        
25975 //        for (var i =0 ; i < editor.toolbars.length;i++) {
25976 //            editor.toolbars[i] = Roo.factory(
25977 //                    typeof(editor.toolbars[i]) == 'string' ?
25978 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
25979 //                Roo.bootstrap.HtmlEditor);
25980 //            editor.toolbars[i].init(editor);
25981 //        }
25982     },
25983
25984      
25985     // private
25986     onRender : function(ct, position)
25987     {
25988        // Roo.log("Call onRender: " + this.xtype);
25989         var _t = this;
25990         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25991       
25992         this.wrap = this.inputEl().wrap({
25993             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25994         });
25995         
25996         this.editorcore.onRender(ct, position);
25997          
25998         if (this.resizable) {
25999             this.resizeEl = new Roo.Resizable(this.wrap, {
26000                 pinned : true,
26001                 wrap: true,
26002                 dynamic : true,
26003                 minHeight : this.height,
26004                 height: this.height,
26005                 handles : this.resizable,
26006                 width: this.width,
26007                 listeners : {
26008                     resize : function(r, w, h) {
26009                         _t.onResize(w,h); // -something
26010                     }
26011                 }
26012             });
26013             
26014         }
26015         this.createToolbar(this);
26016        
26017         
26018         if(!this.width && this.resizable){
26019             this.setSize(this.wrap.getSize());
26020         }
26021         if (this.resizeEl) {
26022             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26023             // should trigger onReize..
26024         }
26025         
26026     },
26027
26028     // private
26029     onResize : function(w, h)
26030     {
26031         Roo.log('resize: ' +w + ',' + h );
26032         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26033         var ew = false;
26034         var eh = false;
26035         
26036         if(this.inputEl() ){
26037             if(typeof w == 'number'){
26038                 var aw = w - this.wrap.getFrameWidth('lr');
26039                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26040                 ew = aw;
26041             }
26042             if(typeof h == 'number'){
26043                  var tbh = -11;  // fixme it needs to tool bar size!
26044                 for (var i =0; i < this.toolbars.length;i++) {
26045                     // fixme - ask toolbars for heights?
26046                     tbh += this.toolbars[i].el.getHeight();
26047                     //if (this.toolbars[i].footer) {
26048                     //    tbh += this.toolbars[i].footer.el.getHeight();
26049                     //}
26050                 }
26051               
26052                 
26053                 
26054                 
26055                 
26056                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26057                 ah -= 5; // knock a few pixes off for look..
26058                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26059                 var eh = ah;
26060             }
26061         }
26062         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26063         this.editorcore.onResize(ew,eh);
26064         
26065     },
26066
26067     /**
26068      * Toggles the editor between standard and source edit mode.
26069      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26070      */
26071     toggleSourceEdit : function(sourceEditMode)
26072     {
26073         this.editorcore.toggleSourceEdit(sourceEditMode);
26074         
26075         if(this.editorcore.sourceEditMode){
26076             Roo.log('editor - showing textarea');
26077             
26078 //            Roo.log('in');
26079 //            Roo.log(this.syncValue());
26080             this.syncValue();
26081             this.inputEl().removeClass(['hide', 'x-hidden']);
26082             this.inputEl().dom.removeAttribute('tabIndex');
26083             this.inputEl().focus();
26084         }else{
26085             Roo.log('editor - hiding textarea');
26086 //            Roo.log('out')
26087 //            Roo.log(this.pushValue()); 
26088             this.pushValue();
26089             
26090             this.inputEl().addClass(['hide', 'x-hidden']);
26091             this.inputEl().dom.setAttribute('tabIndex', -1);
26092             //this.deferFocus();
26093         }
26094          
26095         if(this.resizable){
26096             this.setSize(this.wrap.getSize());
26097         }
26098         
26099         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26100     },
26101  
26102     // private (for BoxComponent)
26103     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26104
26105     // private (for BoxComponent)
26106     getResizeEl : function(){
26107         return this.wrap;
26108     },
26109
26110     // private (for BoxComponent)
26111     getPositionEl : function(){
26112         return this.wrap;
26113     },
26114
26115     // private
26116     initEvents : function(){
26117         this.originalValue = this.getValue();
26118     },
26119
26120 //    /**
26121 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26122 //     * @method
26123 //     */
26124 //    markInvalid : Roo.emptyFn,
26125 //    /**
26126 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26127 //     * @method
26128 //     */
26129 //    clearInvalid : Roo.emptyFn,
26130
26131     setValue : function(v){
26132         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26133         this.editorcore.pushValue();
26134     },
26135
26136      
26137     // private
26138     deferFocus : function(){
26139         this.focus.defer(10, this);
26140     },
26141
26142     // doc'ed in Field
26143     focus : function(){
26144         this.editorcore.focus();
26145         
26146     },
26147       
26148
26149     // private
26150     onDestroy : function(){
26151         
26152         
26153         
26154         if(this.rendered){
26155             
26156             for (var i =0; i < this.toolbars.length;i++) {
26157                 // fixme - ask toolbars for heights?
26158                 this.toolbars[i].onDestroy();
26159             }
26160             
26161             this.wrap.dom.innerHTML = '';
26162             this.wrap.remove();
26163         }
26164     },
26165
26166     // private
26167     onFirstFocus : function(){
26168         //Roo.log("onFirstFocus");
26169         this.editorcore.onFirstFocus();
26170          for (var i =0; i < this.toolbars.length;i++) {
26171             this.toolbars[i].onFirstFocus();
26172         }
26173         
26174     },
26175     
26176     // private
26177     syncValue : function()
26178     {   
26179         this.editorcore.syncValue();
26180     },
26181     
26182     pushValue : function()
26183     {   
26184         this.editorcore.pushValue();
26185     }
26186      
26187     
26188     // hide stuff that is not compatible
26189     /**
26190      * @event blur
26191      * @hide
26192      */
26193     /**
26194      * @event change
26195      * @hide
26196      */
26197     /**
26198      * @event focus
26199      * @hide
26200      */
26201     /**
26202      * @event specialkey
26203      * @hide
26204      */
26205     /**
26206      * @cfg {String} fieldClass @hide
26207      */
26208     /**
26209      * @cfg {String} focusClass @hide
26210      */
26211     /**
26212      * @cfg {String} autoCreate @hide
26213      */
26214     /**
26215      * @cfg {String} inputType @hide
26216      */
26217      
26218     /**
26219      * @cfg {String} invalidText @hide
26220      */
26221     /**
26222      * @cfg {String} msgFx @hide
26223      */
26224     /**
26225      * @cfg {String} validateOnBlur @hide
26226      */
26227 });
26228  
26229     
26230    
26231    
26232    
26233       
26234 Roo.namespace('Roo.bootstrap.htmleditor');
26235 /**
26236  * @class Roo.bootstrap.HtmlEditorToolbar1
26237  * Basic Toolbar
26238  * 
26239  * @example
26240  * Usage:
26241  *
26242  new Roo.bootstrap.HtmlEditor({
26243     ....
26244     toolbars : [
26245         new Roo.bootstrap.HtmlEditorToolbar1({
26246             disable : { fonts: 1 , format: 1, ..., ... , ...],
26247             btns : [ .... ]
26248         })
26249     }
26250      
26251  * 
26252  * @cfg {Object} disable List of elements to disable..
26253  * @cfg {Array} btns List of additional buttons.
26254  * 
26255  * 
26256  * NEEDS Extra CSS? 
26257  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26258  */
26259  
26260 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26261 {
26262     
26263     Roo.apply(this, config);
26264     
26265     // default disabled, based on 'good practice'..
26266     this.disable = this.disable || {};
26267     Roo.applyIf(this.disable, {
26268         fontSize : true,
26269         colors : true,
26270         specialElements : true
26271     });
26272     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26273     
26274     this.editor = config.editor;
26275     this.editorcore = config.editor.editorcore;
26276     
26277     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26278     
26279     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26280     // dont call parent... till later.
26281 }
26282 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26283      
26284     bar : true,
26285     
26286     editor : false,
26287     editorcore : false,
26288     
26289     
26290     formats : [
26291         "p" ,  
26292         "h1","h2","h3","h4","h5","h6", 
26293         "pre", "code", 
26294         "abbr", "acronym", "address", "cite", "samp", "var",
26295         'div','span'
26296     ],
26297     
26298     onRender : function(ct, position)
26299     {
26300        // Roo.log("Call onRender: " + this.xtype);
26301         
26302        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26303        Roo.log(this.el);
26304        this.el.dom.style.marginBottom = '0';
26305        var _this = this;
26306        var editorcore = this.editorcore;
26307        var editor= this.editor;
26308        
26309        var children = [];
26310        var btn = function(id,cmd , toggle, handler, html){
26311        
26312             var  event = toggle ? 'toggle' : 'click';
26313        
26314             var a = {
26315                 size : 'sm',
26316                 xtype: 'Button',
26317                 xns: Roo.bootstrap,
26318                 //glyphicon : id,
26319                 fa: id,
26320                 cmd : id || cmd,
26321                 enableToggle:toggle !== false,
26322                 html : html || '',
26323                 pressed : toggle ? false : null,
26324                 listeners : {}
26325             };
26326             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26327                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26328             };
26329             children.push(a);
26330             return a;
26331        }
26332        
26333     //    var cb_box = function...
26334         
26335         var style = {
26336                 xtype: 'Button',
26337                 size : 'sm',
26338                 xns: Roo.bootstrap,
26339                 fa : 'font',
26340                 //html : 'submit'
26341                 menu : {
26342                     xtype: 'Menu',
26343                     xns: Roo.bootstrap,
26344                     items:  []
26345                 }
26346         };
26347         Roo.each(this.formats, function(f) {
26348             style.menu.items.push({
26349                 xtype :'MenuItem',
26350                 xns: Roo.bootstrap,
26351                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26352                 tagname : f,
26353                 listeners : {
26354                     click : function()
26355                     {
26356                         editorcore.insertTag(this.tagname);
26357                         editor.focus();
26358                     }
26359                 }
26360                 
26361             });
26362         });
26363         children.push(style);   
26364         
26365         btn('bold',false,true);
26366         btn('italic',false,true);
26367         btn('align-left', 'justifyleft',true);
26368         btn('align-center', 'justifycenter',true);
26369         btn('align-right' , 'justifyright',true);
26370         btn('link', false, false, function(btn) {
26371             //Roo.log("create link?");
26372             var url = prompt(this.createLinkText, this.defaultLinkValue);
26373             if(url && url != 'http:/'+'/'){
26374                 this.editorcore.relayCmd('createlink', url);
26375             }
26376         }),
26377         btn('list','insertunorderedlist',true);
26378         btn('pencil', false,true, function(btn){
26379                 Roo.log(this);
26380                 this.toggleSourceEdit(btn.pressed);
26381         });
26382         
26383         if (this.editor.btns.length > 0) {
26384             for (var i = 0; i<this.editor.btns.length; i++) {
26385                 children.push(this.editor.btns[i]);
26386             }
26387         }
26388         
26389         /*
26390         var cog = {
26391                 xtype: 'Button',
26392                 size : 'sm',
26393                 xns: Roo.bootstrap,
26394                 glyphicon : 'cog',
26395                 //html : 'submit'
26396                 menu : {
26397                     xtype: 'Menu',
26398                     xns: Roo.bootstrap,
26399                     items:  []
26400                 }
26401         };
26402         
26403         cog.menu.items.push({
26404             xtype :'MenuItem',
26405             xns: Roo.bootstrap,
26406             html : Clean styles,
26407             tagname : f,
26408             listeners : {
26409                 click : function()
26410                 {
26411                     editorcore.insertTag(this.tagname);
26412                     editor.focus();
26413                 }
26414             }
26415             
26416         });
26417        */
26418         
26419          
26420        this.xtype = 'NavSimplebar';
26421         
26422         for(var i=0;i< children.length;i++) {
26423             
26424             this.buttons.add(this.addxtypeChild(children[i]));
26425             
26426         }
26427         
26428         editor.on('editorevent', this.updateToolbar, this);
26429     },
26430     onBtnClick : function(id)
26431     {
26432        this.editorcore.relayCmd(id);
26433        this.editorcore.focus();
26434     },
26435     
26436     /**
26437      * Protected method that will not generally be called directly. It triggers
26438      * a toolbar update by reading the markup state of the current selection in the editor.
26439      */
26440     updateToolbar: function(){
26441
26442         if(!this.editorcore.activated){
26443             this.editor.onFirstFocus(); // is this neeed?
26444             return;
26445         }
26446
26447         var btns = this.buttons; 
26448         var doc = this.editorcore.doc;
26449         btns.get('bold').setActive(doc.queryCommandState('bold'));
26450         btns.get('italic').setActive(doc.queryCommandState('italic'));
26451         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26452         
26453         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26454         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26455         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26456         
26457         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26458         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26459          /*
26460         
26461         var ans = this.editorcore.getAllAncestors();
26462         if (this.formatCombo) {
26463             
26464             
26465             var store = this.formatCombo.store;
26466             this.formatCombo.setValue("");
26467             for (var i =0; i < ans.length;i++) {
26468                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26469                     // select it..
26470                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26471                     break;
26472                 }
26473             }
26474         }
26475         
26476         
26477         
26478         // hides menus... - so this cant be on a menu...
26479         Roo.bootstrap.MenuMgr.hideAll();
26480         */
26481         Roo.bootstrap.MenuMgr.hideAll();
26482         //this.editorsyncValue();
26483     },
26484     onFirstFocus: function() {
26485         this.buttons.each(function(item){
26486            item.enable();
26487         });
26488     },
26489     toggleSourceEdit : function(sourceEditMode){
26490         
26491           
26492         if(sourceEditMode){
26493             Roo.log("disabling buttons");
26494            this.buttons.each( function(item){
26495                 if(item.cmd != 'pencil'){
26496                     item.disable();
26497                 }
26498             });
26499           
26500         }else{
26501             Roo.log("enabling buttons");
26502             if(this.editorcore.initialized){
26503                 this.buttons.each( function(item){
26504                     item.enable();
26505                 });
26506             }
26507             
26508         }
26509         Roo.log("calling toggole on editor");
26510         // tell the editor that it's been pressed..
26511         this.editor.toggleSourceEdit(sourceEditMode);
26512        
26513     }
26514 });
26515
26516
26517
26518
26519  
26520 /*
26521  * - LGPL
26522  */
26523
26524 /**
26525  * @class Roo.bootstrap.Markdown
26526  * @extends Roo.bootstrap.TextArea
26527  * Bootstrap Showdown editable area
26528  * @cfg {string} content
26529  * 
26530  * @constructor
26531  * Create a new Showdown
26532  */
26533
26534 Roo.bootstrap.Markdown = function(config){
26535     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26536    
26537 };
26538
26539 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26540     
26541     editing :false,
26542     
26543     initEvents : function()
26544     {
26545         
26546         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26547         this.markdownEl = this.el.createChild({
26548             cls : 'roo-markdown-area'
26549         });
26550         this.inputEl().addClass('d-none');
26551         if (this.getValue() == '') {
26552             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26553             
26554         } else {
26555             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26556         }
26557         this.markdownEl.on('click', this.toggleTextEdit, this);
26558         this.on('blur', this.toggleTextEdit, this);
26559         this.on('specialkey', this.resizeTextArea, this);
26560     },
26561     
26562     toggleTextEdit : function()
26563     {
26564         var sh = this.markdownEl.getHeight();
26565         this.inputEl().addClass('d-none');
26566         this.markdownEl.addClass('d-none');
26567         if (!this.editing) {
26568             // show editor?
26569             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26570             this.inputEl().removeClass('d-none');
26571             this.inputEl().focus();
26572             this.editing = true;
26573             return;
26574         }
26575         // show showdown...
26576         this.updateMarkdown();
26577         this.markdownEl.removeClass('d-none');
26578         this.editing = false;
26579         return;
26580     },
26581     updateMarkdown : function()
26582     {
26583         if (this.getValue() == '') {
26584             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26585             return;
26586         }
26587  
26588         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26589     },
26590     
26591     resizeTextArea: function () {
26592         
26593         var sh = 100;
26594         Roo.log([sh, this.getValue().split("\n").length * 30]);
26595         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26596     },
26597     setValue : function(val)
26598     {
26599         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26600         if (!this.editing) {
26601             this.updateMarkdown();
26602         }
26603         
26604     },
26605     focus : function()
26606     {
26607         if (!this.editing) {
26608             this.toggleTextEdit();
26609         }
26610         
26611     }
26612
26613
26614 });
26615 /**
26616  * @class Roo.bootstrap.Table.AbstractSelectionModel
26617  * @extends Roo.util.Observable
26618  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26619  * implemented by descendant classes.  This class should not be directly instantiated.
26620  * @constructor
26621  */
26622 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26623     this.locked = false;
26624     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26625 };
26626
26627
26628 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26629     /** @ignore Called by the grid automatically. Do not call directly. */
26630     init : function(grid){
26631         this.grid = grid;
26632         this.initEvents();
26633     },
26634
26635     /**
26636      * Locks the selections.
26637      */
26638     lock : function(){
26639         this.locked = true;
26640     },
26641
26642     /**
26643      * Unlocks the selections.
26644      */
26645     unlock : function(){
26646         this.locked = false;
26647     },
26648
26649     /**
26650      * Returns true if the selections are locked.
26651      * @return {Boolean}
26652      */
26653     isLocked : function(){
26654         return this.locked;
26655     },
26656     
26657     
26658     initEvents : function ()
26659     {
26660         
26661     }
26662 });
26663 /**
26664  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26665  * @class Roo.bootstrap.Table.RowSelectionModel
26666  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26667  * It supports multiple selections and keyboard selection/navigation. 
26668  * @constructor
26669  * @param {Object} config
26670  */
26671
26672 Roo.bootstrap.Table.RowSelectionModel = function(config){
26673     Roo.apply(this, config);
26674     this.selections = new Roo.util.MixedCollection(false, function(o){
26675         return o.id;
26676     });
26677
26678     this.last = false;
26679     this.lastActive = false;
26680
26681     this.addEvents({
26682         /**
26683              * @event selectionchange
26684              * Fires when the selection changes
26685              * @param {SelectionModel} this
26686              */
26687             "selectionchange" : true,
26688         /**
26689              * @event afterselectionchange
26690              * Fires after the selection changes (eg. by key press or clicking)
26691              * @param {SelectionModel} this
26692              */
26693             "afterselectionchange" : true,
26694         /**
26695              * @event beforerowselect
26696              * Fires when a row is selected being selected, return false to cancel.
26697              * @param {SelectionModel} this
26698              * @param {Number} rowIndex The selected index
26699              * @param {Boolean} keepExisting False if other selections will be cleared
26700              */
26701             "beforerowselect" : true,
26702         /**
26703              * @event rowselect
26704              * Fires when a row is selected.
26705              * @param {SelectionModel} this
26706              * @param {Number} rowIndex The selected index
26707              * @param {Roo.data.Record} r The record
26708              */
26709             "rowselect" : true,
26710         /**
26711              * @event rowdeselect
26712              * Fires when a row is deselected.
26713              * @param {SelectionModel} this
26714              * @param {Number} rowIndex The selected index
26715              */
26716         "rowdeselect" : true
26717     });
26718     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26719     this.locked = false;
26720  };
26721
26722 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26723     /**
26724      * @cfg {Boolean} singleSelect
26725      * True to allow selection of only one row at a time (defaults to false)
26726      */
26727     singleSelect : false,
26728
26729     // private
26730     initEvents : function()
26731     {
26732
26733         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26734         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26735         //}else{ // allow click to work like normal
26736          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26737         //}
26738         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26739         this.grid.on("rowclick", this.handleMouseDown, this);
26740         
26741         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26742             "up" : function(e){
26743                 if(!e.shiftKey){
26744                     this.selectPrevious(e.shiftKey);
26745                 }else if(this.last !== false && this.lastActive !== false){
26746                     var last = this.last;
26747                     this.selectRange(this.last,  this.lastActive-1);
26748                     this.grid.getView().focusRow(this.lastActive);
26749                     if(last !== false){
26750                         this.last = last;
26751                     }
26752                 }else{
26753                     this.selectFirstRow();
26754                 }
26755                 this.fireEvent("afterselectionchange", this);
26756             },
26757             "down" : function(e){
26758                 if(!e.shiftKey){
26759                     this.selectNext(e.shiftKey);
26760                 }else if(this.last !== false && this.lastActive !== false){
26761                     var last = this.last;
26762                     this.selectRange(this.last,  this.lastActive+1);
26763                     this.grid.getView().focusRow(this.lastActive);
26764                     if(last !== false){
26765                         this.last = last;
26766                     }
26767                 }else{
26768                     this.selectFirstRow();
26769                 }
26770                 this.fireEvent("afterselectionchange", this);
26771             },
26772             scope: this
26773         });
26774         this.grid.store.on('load', function(){
26775             this.selections.clear();
26776         },this);
26777         /*
26778         var view = this.grid.view;
26779         view.on("refresh", this.onRefresh, this);
26780         view.on("rowupdated", this.onRowUpdated, this);
26781         view.on("rowremoved", this.onRemove, this);
26782         */
26783     },
26784
26785     // private
26786     onRefresh : function()
26787     {
26788         var ds = this.grid.store, i, v = this.grid.view;
26789         var s = this.selections;
26790         s.each(function(r){
26791             if((i = ds.indexOfId(r.id)) != -1){
26792                 v.onRowSelect(i);
26793             }else{
26794                 s.remove(r);
26795             }
26796         });
26797     },
26798
26799     // private
26800     onRemove : function(v, index, r){
26801         this.selections.remove(r);
26802     },
26803
26804     // private
26805     onRowUpdated : function(v, index, r){
26806         if(this.isSelected(r)){
26807             v.onRowSelect(index);
26808         }
26809     },
26810
26811     /**
26812      * Select records.
26813      * @param {Array} records The records to select
26814      * @param {Boolean} keepExisting (optional) True to keep existing selections
26815      */
26816     selectRecords : function(records, keepExisting)
26817     {
26818         if(!keepExisting){
26819             this.clearSelections();
26820         }
26821             var ds = this.grid.store;
26822         for(var i = 0, len = records.length; i < len; i++){
26823             this.selectRow(ds.indexOf(records[i]), true);
26824         }
26825     },
26826
26827     /**
26828      * Gets the number of selected rows.
26829      * @return {Number}
26830      */
26831     getCount : function(){
26832         return this.selections.length;
26833     },
26834
26835     /**
26836      * Selects the first row in the grid.
26837      */
26838     selectFirstRow : function(){
26839         this.selectRow(0);
26840     },
26841
26842     /**
26843      * Select the last row.
26844      * @param {Boolean} keepExisting (optional) True to keep existing selections
26845      */
26846     selectLastRow : function(keepExisting){
26847         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26848         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26849     },
26850
26851     /**
26852      * Selects the row immediately following the last selected row.
26853      * @param {Boolean} keepExisting (optional) True to keep existing selections
26854      */
26855     selectNext : function(keepExisting)
26856     {
26857             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26858             this.selectRow(this.last+1, keepExisting);
26859             this.grid.getView().focusRow(this.last);
26860         }
26861     },
26862
26863     /**
26864      * Selects the row that precedes the last selected row.
26865      * @param {Boolean} keepExisting (optional) True to keep existing selections
26866      */
26867     selectPrevious : function(keepExisting){
26868         if(this.last){
26869             this.selectRow(this.last-1, keepExisting);
26870             this.grid.getView().focusRow(this.last);
26871         }
26872     },
26873
26874     /**
26875      * Returns the selected records
26876      * @return {Array} Array of selected records
26877      */
26878     getSelections : function(){
26879         return [].concat(this.selections.items);
26880     },
26881
26882     /**
26883      * Returns the first selected record.
26884      * @return {Record}
26885      */
26886     getSelected : function(){
26887         return this.selections.itemAt(0);
26888     },
26889
26890
26891     /**
26892      * Clears all selections.
26893      */
26894     clearSelections : function(fast)
26895     {
26896         if(this.locked) {
26897             return;
26898         }
26899         if(fast !== true){
26900                 var ds = this.grid.store;
26901             var s = this.selections;
26902             s.each(function(r){
26903                 this.deselectRow(ds.indexOfId(r.id));
26904             }, this);
26905             s.clear();
26906         }else{
26907             this.selections.clear();
26908         }
26909         this.last = false;
26910     },
26911
26912
26913     /**
26914      * Selects all rows.
26915      */
26916     selectAll : function(){
26917         if(this.locked) {
26918             return;
26919         }
26920         this.selections.clear();
26921         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26922             this.selectRow(i, true);
26923         }
26924     },
26925
26926     /**
26927      * Returns True if there is a selection.
26928      * @return {Boolean}
26929      */
26930     hasSelection : function(){
26931         return this.selections.length > 0;
26932     },
26933
26934     /**
26935      * Returns True if the specified row is selected.
26936      * @param {Number/Record} record The record or index of the record to check
26937      * @return {Boolean}
26938      */
26939     isSelected : function(index){
26940             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26941         return (r && this.selections.key(r.id) ? true : false);
26942     },
26943
26944     /**
26945      * Returns True if the specified record id is selected.
26946      * @param {String} id The id of record to check
26947      * @return {Boolean}
26948      */
26949     isIdSelected : function(id){
26950         return (this.selections.key(id) ? true : false);
26951     },
26952
26953
26954     // private
26955     handleMouseDBClick : function(e, t){
26956         
26957     },
26958     // private
26959     handleMouseDown : function(e, t)
26960     {
26961             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26962         if(this.isLocked() || rowIndex < 0 ){
26963             return;
26964         };
26965         if(e.shiftKey && this.last !== false){
26966             var last = this.last;
26967             this.selectRange(last, rowIndex, e.ctrlKey);
26968             this.last = last; // reset the last
26969             t.focus();
26970     
26971         }else{
26972             var isSelected = this.isSelected(rowIndex);
26973             //Roo.log("select row:" + rowIndex);
26974             if(isSelected){
26975                 this.deselectRow(rowIndex);
26976             } else {
26977                         this.selectRow(rowIndex, true);
26978             }
26979     
26980             /*
26981                 if(e.button !== 0 && isSelected){
26982                 alert('rowIndex 2: ' + rowIndex);
26983                     view.focusRow(rowIndex);
26984                 }else if(e.ctrlKey && isSelected){
26985                     this.deselectRow(rowIndex);
26986                 }else if(!isSelected){
26987                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26988                     view.focusRow(rowIndex);
26989                 }
26990             */
26991         }
26992         this.fireEvent("afterselectionchange", this);
26993     },
26994     // private
26995     handleDragableRowClick :  function(grid, rowIndex, e) 
26996     {
26997         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26998             this.selectRow(rowIndex, false);
26999             grid.view.focusRow(rowIndex);
27000              this.fireEvent("afterselectionchange", this);
27001         }
27002     },
27003     
27004     /**
27005      * Selects multiple rows.
27006      * @param {Array} rows Array of the indexes of the row to select
27007      * @param {Boolean} keepExisting (optional) True to keep existing selections
27008      */
27009     selectRows : function(rows, keepExisting){
27010         if(!keepExisting){
27011             this.clearSelections();
27012         }
27013         for(var i = 0, len = rows.length; i < len; i++){
27014             this.selectRow(rows[i], true);
27015         }
27016     },
27017
27018     /**
27019      * Selects a range of rows. All rows in between startRow and endRow are also selected.
27020      * @param {Number} startRow The index of the first row in the range
27021      * @param {Number} endRow The index of the last row in the range
27022      * @param {Boolean} keepExisting (optional) True to retain existing selections
27023      */
27024     selectRange : function(startRow, endRow, keepExisting){
27025         if(this.locked) {
27026             return;
27027         }
27028         if(!keepExisting){
27029             this.clearSelections();
27030         }
27031         if(startRow <= endRow){
27032             for(var i = startRow; i <= endRow; i++){
27033                 this.selectRow(i, true);
27034             }
27035         }else{
27036             for(var i = startRow; i >= endRow; i--){
27037                 this.selectRow(i, true);
27038             }
27039         }
27040     },
27041
27042     /**
27043      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27044      * @param {Number} startRow The index of the first row in the range
27045      * @param {Number} endRow The index of the last row in the range
27046      */
27047     deselectRange : function(startRow, endRow, preventViewNotify){
27048         if(this.locked) {
27049             return;
27050         }
27051         for(var i = startRow; i <= endRow; i++){
27052             this.deselectRow(i, preventViewNotify);
27053         }
27054     },
27055
27056     /**
27057      * Selects a row.
27058      * @param {Number} row The index of the row to select
27059      * @param {Boolean} keepExisting (optional) True to keep existing selections
27060      */
27061     selectRow : function(index, keepExisting, preventViewNotify)
27062     {
27063             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27064             return;
27065         }
27066         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27067             if(!keepExisting || this.singleSelect){
27068                 this.clearSelections();
27069             }
27070             
27071             var r = this.grid.store.getAt(index);
27072             //console.log('selectRow - record id :' + r.id);
27073             
27074             this.selections.add(r);
27075             this.last = this.lastActive = index;
27076             if(!preventViewNotify){
27077                 var proxy = new Roo.Element(
27078                                 this.grid.getRowDom(index)
27079                 );
27080                 proxy.addClass('bg-info info');
27081             }
27082             this.fireEvent("rowselect", this, index, r);
27083             this.fireEvent("selectionchange", this);
27084         }
27085     },
27086
27087     /**
27088      * Deselects a row.
27089      * @param {Number} row The index of the row to deselect
27090      */
27091     deselectRow : function(index, preventViewNotify)
27092     {
27093         if(this.locked) {
27094             return;
27095         }
27096         if(this.last == index){
27097             this.last = false;
27098         }
27099         if(this.lastActive == index){
27100             this.lastActive = false;
27101         }
27102         
27103         var r = this.grid.store.getAt(index);
27104         if (!r) {
27105             return;
27106         }
27107         
27108         this.selections.remove(r);
27109         //.console.log('deselectRow - record id :' + r.id);
27110         if(!preventViewNotify){
27111         
27112             var proxy = new Roo.Element(
27113                 this.grid.getRowDom(index)
27114             );
27115             proxy.removeClass('bg-info info');
27116         }
27117         this.fireEvent("rowdeselect", this, index);
27118         this.fireEvent("selectionchange", this);
27119     },
27120
27121     // private
27122     restoreLast : function(){
27123         if(this._last){
27124             this.last = this._last;
27125         }
27126     },
27127
27128     // private
27129     acceptsNav : function(row, col, cm){
27130         return !cm.isHidden(col) && cm.isCellEditable(col, row);
27131     },
27132
27133     // private
27134     onEditorKey : function(field, e){
27135         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27136         if(k == e.TAB){
27137             e.stopEvent();
27138             ed.completeEdit();
27139             if(e.shiftKey){
27140                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27141             }else{
27142                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27143             }
27144         }else if(k == e.ENTER && !e.ctrlKey){
27145             e.stopEvent();
27146             ed.completeEdit();
27147             if(e.shiftKey){
27148                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27149             }else{
27150                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27151             }
27152         }else if(k == e.ESC){
27153             ed.cancelEdit();
27154         }
27155         if(newCell){
27156             g.startEditing(newCell[0], newCell[1]);
27157         }
27158     }
27159 });
27160 /*
27161  * Based on:
27162  * Ext JS Library 1.1.1
27163  * Copyright(c) 2006-2007, Ext JS, LLC.
27164  *
27165  * Originally Released Under LGPL - original licence link has changed is not relivant.
27166  *
27167  * Fork - LGPL
27168  * <script type="text/javascript">
27169  */
27170  
27171 /**
27172  * @class Roo.bootstrap.PagingToolbar
27173  * @extends Roo.bootstrap.NavSimplebar
27174  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27175  * @constructor
27176  * Create a new PagingToolbar
27177  * @param {Object} config The config object
27178  * @param {Roo.data.Store} store
27179  */
27180 Roo.bootstrap.PagingToolbar = function(config)
27181 {
27182     // old args format still supported... - xtype is prefered..
27183         // created from xtype...
27184     
27185     this.ds = config.dataSource;
27186     
27187     if (config.store && !this.ds) {
27188         this.store= Roo.factory(config.store, Roo.data);
27189         this.ds = this.store;
27190         this.ds.xmodule = this.xmodule || false;
27191     }
27192     
27193     this.toolbarItems = [];
27194     if (config.items) {
27195         this.toolbarItems = config.items;
27196     }
27197     
27198     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27199     
27200     this.cursor = 0;
27201     
27202     if (this.ds) { 
27203         this.bind(this.ds);
27204     }
27205     
27206     if (Roo.bootstrap.version == 4) {
27207         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27208     } else {
27209         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27210     }
27211     
27212 };
27213
27214 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27215     /**
27216      * @cfg {Roo.data.Store} dataSource
27217      * The underlying data store providing the paged data
27218      */
27219     /**
27220      * @cfg {String/HTMLElement/Element} container
27221      * container The id or element that will contain the toolbar
27222      */
27223     /**
27224      * @cfg {Boolean} displayInfo
27225      * True to display the displayMsg (defaults to false)
27226      */
27227     /**
27228      * @cfg {Number} pageSize
27229      * The number of records to display per page (defaults to 20)
27230      */
27231     pageSize: 20,
27232     /**
27233      * @cfg {String} displayMsg
27234      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27235      */
27236     displayMsg : 'Displaying {0} - {1} of {2}',
27237     /**
27238      * @cfg {String} emptyMsg
27239      * The message to display when no records are found (defaults to "No data to display")
27240      */
27241     emptyMsg : 'No data to display',
27242     /**
27243      * Customizable piece of the default paging text (defaults to "Page")
27244      * @type String
27245      */
27246     beforePageText : "Page",
27247     /**
27248      * Customizable piece of the default paging text (defaults to "of %0")
27249      * @type String
27250      */
27251     afterPageText : "of {0}",
27252     /**
27253      * Customizable piece of the default paging text (defaults to "First Page")
27254      * @type String
27255      */
27256     firstText : "First Page",
27257     /**
27258      * Customizable piece of the default paging text (defaults to "Previous Page")
27259      * @type String
27260      */
27261     prevText : "Previous Page",
27262     /**
27263      * Customizable piece of the default paging text (defaults to "Next Page")
27264      * @type String
27265      */
27266     nextText : "Next Page",
27267     /**
27268      * Customizable piece of the default paging text (defaults to "Last Page")
27269      * @type String
27270      */
27271     lastText : "Last Page",
27272     /**
27273      * Customizable piece of the default paging text (defaults to "Refresh")
27274      * @type String
27275      */
27276     refreshText : "Refresh",
27277
27278     buttons : false,
27279     // private
27280     onRender : function(ct, position) 
27281     {
27282         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27283         this.navgroup.parentId = this.id;
27284         this.navgroup.onRender(this.el, null);
27285         // add the buttons to the navgroup
27286         
27287         if(this.displayInfo){
27288             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27289             this.displayEl = this.el.select('.x-paging-info', true).first();
27290 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27291 //            this.displayEl = navel.el.select('span',true).first();
27292         }
27293         
27294         var _this = this;
27295         
27296         if(this.buttons){
27297             Roo.each(_this.buttons, function(e){ // this might need to use render????
27298                Roo.factory(e).render(_this.el);
27299             });
27300         }
27301             
27302         Roo.each(_this.toolbarItems, function(e) {
27303             _this.navgroup.addItem(e);
27304         });
27305         
27306         
27307         this.first = this.navgroup.addItem({
27308             tooltip: this.firstText,
27309             cls: "prev btn-outline-secondary",
27310             html : ' <i class="fa fa-step-backward"></i>',
27311             disabled: true,
27312             preventDefault: true,
27313             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27314         });
27315         
27316         this.prev =  this.navgroup.addItem({
27317             tooltip: this.prevText,
27318             cls: "prev btn-outline-secondary",
27319             html : ' <i class="fa fa-backward"></i>',
27320             disabled: true,
27321             preventDefault: true,
27322             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27323         });
27324     //this.addSeparator();
27325         
27326         
27327         var field = this.navgroup.addItem( {
27328             tagtype : 'span',
27329             cls : 'x-paging-position  btn-outline-secondary',
27330              disabled: true,
27331             html : this.beforePageText  +
27332                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27333                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27334          } ); //?? escaped?
27335         
27336         this.field = field.el.select('input', true).first();
27337         this.field.on("keydown", this.onPagingKeydown, this);
27338         this.field.on("focus", function(){this.dom.select();});
27339     
27340     
27341         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27342         //this.field.setHeight(18);
27343         //this.addSeparator();
27344         this.next = this.navgroup.addItem({
27345             tooltip: this.nextText,
27346             cls: "next btn-outline-secondary",
27347             html : ' <i class="fa fa-forward"></i>',
27348             disabled: true,
27349             preventDefault: true,
27350             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27351         });
27352         this.last = this.navgroup.addItem({
27353             tooltip: this.lastText,
27354             html : ' <i class="fa fa-step-forward"></i>',
27355             cls: "next btn-outline-secondary",
27356             disabled: true,
27357             preventDefault: true,
27358             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27359         });
27360     //this.addSeparator();
27361         this.loading = this.navgroup.addItem({
27362             tooltip: this.refreshText,
27363             cls: "btn-outline-secondary",
27364             html : ' <i class="fa fa-refresh"></i>',
27365             preventDefault: true,
27366             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27367         });
27368         
27369     },
27370
27371     // private
27372     updateInfo : function(){
27373         if(this.displayEl){
27374             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27375             var msg = count == 0 ?
27376                 this.emptyMsg :
27377                 String.format(
27378                     this.displayMsg,
27379                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27380                 );
27381             this.displayEl.update(msg);
27382         }
27383     },
27384
27385     // private
27386     onLoad : function(ds, r, o)
27387     {
27388         this.cursor = o.params && o.params.start ? o.params.start : 0;
27389         
27390         var d = this.getPageData(),
27391             ap = d.activePage,
27392             ps = d.pages;
27393         
27394         
27395         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27396         this.field.dom.value = ap;
27397         this.first.setDisabled(ap == 1);
27398         this.prev.setDisabled(ap == 1);
27399         this.next.setDisabled(ap == ps);
27400         this.last.setDisabled(ap == ps);
27401         this.loading.enable();
27402         this.updateInfo();
27403     },
27404
27405     // private
27406     getPageData : function(){
27407         var total = this.ds.getTotalCount();
27408         return {
27409             total : total,
27410             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27411             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27412         };
27413     },
27414
27415     // private
27416     onLoadError : function(){
27417         this.loading.enable();
27418     },
27419
27420     // private
27421     onPagingKeydown : function(e){
27422         var k = e.getKey();
27423         var d = this.getPageData();
27424         if(k == e.RETURN){
27425             var v = this.field.dom.value, pageNum;
27426             if(!v || isNaN(pageNum = parseInt(v, 10))){
27427                 this.field.dom.value = d.activePage;
27428                 return;
27429             }
27430             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27431             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27432             e.stopEvent();
27433         }
27434         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))
27435         {
27436           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27437           this.field.dom.value = pageNum;
27438           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27439           e.stopEvent();
27440         }
27441         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27442         {
27443           var v = this.field.dom.value, pageNum; 
27444           var increment = (e.shiftKey) ? 10 : 1;
27445           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27446                 increment *= -1;
27447           }
27448           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27449             this.field.dom.value = d.activePage;
27450             return;
27451           }
27452           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27453           {
27454             this.field.dom.value = parseInt(v, 10) + increment;
27455             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27456             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27457           }
27458           e.stopEvent();
27459         }
27460     },
27461
27462     // private
27463     beforeLoad : function(){
27464         if(this.loading){
27465             this.loading.disable();
27466         }
27467     },
27468
27469     // private
27470     onClick : function(which){
27471         
27472         var ds = this.ds;
27473         if (!ds) {
27474             return;
27475         }
27476         
27477         switch(which){
27478             case "first":
27479                 ds.load({params:{start: 0, limit: this.pageSize}});
27480             break;
27481             case "prev":
27482                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27483             break;
27484             case "next":
27485                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27486             break;
27487             case "last":
27488                 var total = ds.getTotalCount();
27489                 var extra = total % this.pageSize;
27490                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27491                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27492             break;
27493             case "refresh":
27494                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27495             break;
27496         }
27497     },
27498
27499     /**
27500      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27501      * @param {Roo.data.Store} store The data store to unbind
27502      */
27503     unbind : function(ds){
27504         ds.un("beforeload", this.beforeLoad, this);
27505         ds.un("load", this.onLoad, this);
27506         ds.un("loadexception", this.onLoadError, this);
27507         ds.un("remove", this.updateInfo, this);
27508         ds.un("add", this.updateInfo, this);
27509         this.ds = undefined;
27510     },
27511
27512     /**
27513      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27514      * @param {Roo.data.Store} store The data store to bind
27515      */
27516     bind : function(ds){
27517         ds.on("beforeload", this.beforeLoad, this);
27518         ds.on("load", this.onLoad, this);
27519         ds.on("loadexception", this.onLoadError, this);
27520         ds.on("remove", this.updateInfo, this);
27521         ds.on("add", this.updateInfo, this);
27522         this.ds = ds;
27523     }
27524 });/*
27525  * - LGPL
27526  *
27527  * element
27528  * 
27529  */
27530
27531 /**
27532  * @class Roo.bootstrap.MessageBar
27533  * @extends Roo.bootstrap.Component
27534  * Bootstrap MessageBar class
27535  * @cfg {String} html contents of the MessageBar
27536  * @cfg {String} weight (info | success | warning | danger) default info
27537  * @cfg {String} beforeClass insert the bar before the given class
27538  * @cfg {Boolean} closable (true | false) default false
27539  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27540  * 
27541  * @constructor
27542  * Create a new Element
27543  * @param {Object} config The config object
27544  */
27545
27546 Roo.bootstrap.MessageBar = function(config){
27547     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27548 };
27549
27550 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27551     
27552     html: '',
27553     weight: 'info',
27554     closable: false,
27555     fixed: false,
27556     beforeClass: 'bootstrap-sticky-wrap',
27557     
27558     getAutoCreate : function(){
27559         
27560         var cfg = {
27561             tag: 'div',
27562             cls: 'alert alert-dismissable alert-' + this.weight,
27563             cn: [
27564                 {
27565                     tag: 'span',
27566                     cls: 'message',
27567                     html: this.html || ''
27568                 }
27569             ]
27570         };
27571         
27572         if(this.fixed){
27573             cfg.cls += ' alert-messages-fixed';
27574         }
27575         
27576         if(this.closable){
27577             cfg.cn.push({
27578                 tag: 'button',
27579                 cls: 'close',
27580                 html: 'x'
27581             });
27582         }
27583         
27584         return cfg;
27585     },
27586     
27587     onRender : function(ct, position)
27588     {
27589         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27590         
27591         if(!this.el){
27592             var cfg = Roo.apply({},  this.getAutoCreate());
27593             cfg.id = Roo.id();
27594             
27595             if (this.cls) {
27596                 cfg.cls += ' ' + this.cls;
27597             }
27598             if (this.style) {
27599                 cfg.style = this.style;
27600             }
27601             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27602             
27603             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27604         }
27605         
27606         this.el.select('>button.close').on('click', this.hide, this);
27607         
27608     },
27609     
27610     show : function()
27611     {
27612         if (!this.rendered) {
27613             this.render();
27614         }
27615         
27616         this.el.show();
27617         
27618         this.fireEvent('show', this);
27619         
27620     },
27621     
27622     hide : function()
27623     {
27624         if (!this.rendered) {
27625             this.render();
27626         }
27627         
27628         this.el.hide();
27629         
27630         this.fireEvent('hide', this);
27631     },
27632     
27633     update : function()
27634     {
27635 //        var e = this.el.dom.firstChild;
27636 //        
27637 //        if(this.closable){
27638 //            e = e.nextSibling;
27639 //        }
27640 //        
27641 //        e.data = this.html || '';
27642
27643         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27644     }
27645    
27646 });
27647
27648  
27649
27650      /*
27651  * - LGPL
27652  *
27653  * Graph
27654  * 
27655  */
27656
27657
27658 /**
27659  * @class Roo.bootstrap.Graph
27660  * @extends Roo.bootstrap.Component
27661  * Bootstrap Graph class
27662 > Prameters
27663  -sm {number} sm 4
27664  -md {number} md 5
27665  @cfg {String} graphtype  bar | vbar | pie
27666  @cfg {number} g_x coodinator | centre x (pie)
27667  @cfg {number} g_y coodinator | centre y (pie)
27668  @cfg {number} g_r radius (pie)
27669  @cfg {number} g_height height of the chart (respected by all elements in the set)
27670  @cfg {number} g_width width of the chart (respected by all elements in the set)
27671  @cfg {Object} title The title of the chart
27672     
27673  -{Array}  values
27674  -opts (object) options for the chart 
27675      o {
27676      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27677      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27678      o vgutter (number)
27679      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.
27680      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27681      o to
27682      o stretch (boolean)
27683      o }
27684  -opts (object) options for the pie
27685      o{
27686      o cut
27687      o startAngle (number)
27688      o endAngle (number)
27689      } 
27690  *
27691  * @constructor
27692  * Create a new Input
27693  * @param {Object} config The config object
27694  */
27695
27696 Roo.bootstrap.Graph = function(config){
27697     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27698     
27699     this.addEvents({
27700         // img events
27701         /**
27702          * @event click
27703          * The img click event for the img.
27704          * @param {Roo.EventObject} e
27705          */
27706         "click" : true
27707     });
27708 };
27709
27710 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27711     
27712     sm: 4,
27713     md: 5,
27714     graphtype: 'bar',
27715     g_height: 250,
27716     g_width: 400,
27717     g_x: 50,
27718     g_y: 50,
27719     g_r: 30,
27720     opts:{
27721         //g_colors: this.colors,
27722         g_type: 'soft',
27723         g_gutter: '20%'
27724
27725     },
27726     title : false,
27727
27728     getAutoCreate : function(){
27729         
27730         var cfg = {
27731             tag: 'div',
27732             html : null
27733         };
27734         
27735         
27736         return  cfg;
27737     },
27738
27739     onRender : function(ct,position){
27740         
27741         
27742         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27743         
27744         if (typeof(Raphael) == 'undefined') {
27745             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27746             return;
27747         }
27748         
27749         this.raphael = Raphael(this.el.dom);
27750         
27751                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27752                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27753                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27754                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27755                 /*
27756                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27757                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27758                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27759                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27760                 
27761                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27762                 r.barchart(330, 10, 300, 220, data1);
27763                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27764                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27765                 */
27766                 
27767                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27768                 // r.barchart(30, 30, 560, 250,  xdata, {
27769                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27770                 //     axis : "0 0 1 1",
27771                 //     axisxlabels :  xdata
27772                 //     //yvalues : cols,
27773                    
27774                 // });
27775 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27776 //        
27777 //        this.load(null,xdata,{
27778 //                axis : "0 0 1 1",
27779 //                axisxlabels :  xdata
27780 //                });
27781
27782     },
27783
27784     load : function(graphtype,xdata,opts)
27785     {
27786         this.raphael.clear();
27787         if(!graphtype) {
27788             graphtype = this.graphtype;
27789         }
27790         if(!opts){
27791             opts = this.opts;
27792         }
27793         var r = this.raphael,
27794             fin = function () {
27795                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27796             },
27797             fout = function () {
27798                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27799             },
27800             pfin = function() {
27801                 this.sector.stop();
27802                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27803
27804                 if (this.label) {
27805                     this.label[0].stop();
27806                     this.label[0].attr({ r: 7.5 });
27807                     this.label[1].attr({ "font-weight": 800 });
27808                 }
27809             },
27810             pfout = function() {
27811                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27812
27813                 if (this.label) {
27814                     this.label[0].animate({ r: 5 }, 500, "bounce");
27815                     this.label[1].attr({ "font-weight": 400 });
27816                 }
27817             };
27818
27819         switch(graphtype){
27820             case 'bar':
27821                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27822                 break;
27823             case 'hbar':
27824                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27825                 break;
27826             case 'pie':
27827 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27828 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27829 //            
27830                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27831                 
27832                 break;
27833
27834         }
27835         
27836         if(this.title){
27837             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27838         }
27839         
27840     },
27841     
27842     setTitle: function(o)
27843     {
27844         this.title = o;
27845     },
27846     
27847     initEvents: function() {
27848         
27849         if(!this.href){
27850             this.el.on('click', this.onClick, this);
27851         }
27852     },
27853     
27854     onClick : function(e)
27855     {
27856         Roo.log('img onclick');
27857         this.fireEvent('click', this, e);
27858     }
27859    
27860 });
27861
27862  
27863 /*
27864  * - LGPL
27865  *
27866  * numberBox
27867  * 
27868  */
27869 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27870
27871 /**
27872  * @class Roo.bootstrap.dash.NumberBox
27873  * @extends Roo.bootstrap.Component
27874  * Bootstrap NumberBox class
27875  * @cfg {String} headline Box headline
27876  * @cfg {String} content Box content
27877  * @cfg {String} icon Box icon
27878  * @cfg {String} footer Footer text
27879  * @cfg {String} fhref Footer href
27880  * 
27881  * @constructor
27882  * Create a new NumberBox
27883  * @param {Object} config The config object
27884  */
27885
27886
27887 Roo.bootstrap.dash.NumberBox = function(config){
27888     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27889     
27890 };
27891
27892 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27893     
27894     headline : '',
27895     content : '',
27896     icon : '',
27897     footer : '',
27898     fhref : '',
27899     ficon : '',
27900     
27901     getAutoCreate : function(){
27902         
27903         var cfg = {
27904             tag : 'div',
27905             cls : 'small-box ',
27906             cn : [
27907                 {
27908                     tag : 'div',
27909                     cls : 'inner',
27910                     cn :[
27911                         {
27912                             tag : 'h3',
27913                             cls : 'roo-headline',
27914                             html : this.headline
27915                         },
27916                         {
27917                             tag : 'p',
27918                             cls : 'roo-content',
27919                             html : this.content
27920                         }
27921                     ]
27922                 }
27923             ]
27924         };
27925         
27926         if(this.icon){
27927             cfg.cn.push({
27928                 tag : 'div',
27929                 cls : 'icon',
27930                 cn :[
27931                     {
27932                         tag : 'i',
27933                         cls : 'ion ' + this.icon
27934                     }
27935                 ]
27936             });
27937         }
27938         
27939         if(this.footer){
27940             var footer = {
27941                 tag : 'a',
27942                 cls : 'small-box-footer',
27943                 href : this.fhref || '#',
27944                 html : this.footer
27945             };
27946             
27947             cfg.cn.push(footer);
27948             
27949         }
27950         
27951         return  cfg;
27952     },
27953
27954     onRender : function(ct,position){
27955         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27956
27957
27958        
27959                 
27960     },
27961
27962     setHeadline: function (value)
27963     {
27964         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27965     },
27966     
27967     setFooter: function (value, href)
27968     {
27969         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27970         
27971         if(href){
27972             this.el.select('a.small-box-footer',true).first().attr('href', href);
27973         }
27974         
27975     },
27976
27977     setContent: function (value)
27978     {
27979         this.el.select('.roo-content',true).first().dom.innerHTML = value;
27980     },
27981
27982     initEvents: function() 
27983     {   
27984         
27985     }
27986     
27987 });
27988
27989  
27990 /*
27991  * - LGPL
27992  *
27993  * TabBox
27994  * 
27995  */
27996 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27997
27998 /**
27999  * @class Roo.bootstrap.dash.TabBox
28000  * @extends Roo.bootstrap.Component
28001  * Bootstrap TabBox class
28002  * @cfg {String} title Title of the TabBox
28003  * @cfg {String} icon Icon of the TabBox
28004  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28005  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28006  * 
28007  * @constructor
28008  * Create a new TabBox
28009  * @param {Object} config The config object
28010  */
28011
28012
28013 Roo.bootstrap.dash.TabBox = function(config){
28014     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28015     this.addEvents({
28016         // raw events
28017         /**
28018          * @event addpane
28019          * When a pane is added
28020          * @param {Roo.bootstrap.dash.TabPane} pane
28021          */
28022         "addpane" : true,
28023         /**
28024          * @event activatepane
28025          * When a pane is activated
28026          * @param {Roo.bootstrap.dash.TabPane} pane
28027          */
28028         "activatepane" : true
28029         
28030          
28031     });
28032     
28033     this.panes = [];
28034 };
28035
28036 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28037
28038     title : '',
28039     icon : false,
28040     showtabs : true,
28041     tabScrollable : false,
28042     
28043     getChildContainer : function()
28044     {
28045         return this.el.select('.tab-content', true).first();
28046     },
28047     
28048     getAutoCreate : function(){
28049         
28050         var header = {
28051             tag: 'li',
28052             cls: 'pull-left header',
28053             html: this.title,
28054             cn : []
28055         };
28056         
28057         if(this.icon){
28058             header.cn.push({
28059                 tag: 'i',
28060                 cls: 'fa ' + this.icon
28061             });
28062         }
28063         
28064         var h = {
28065             tag: 'ul',
28066             cls: 'nav nav-tabs pull-right',
28067             cn: [
28068                 header
28069             ]
28070         };
28071         
28072         if(this.tabScrollable){
28073             h = {
28074                 tag: 'div',
28075                 cls: 'tab-header',
28076                 cn: [
28077                     {
28078                         tag: 'ul',
28079                         cls: 'nav nav-tabs pull-right',
28080                         cn: [
28081                             header
28082                         ]
28083                     }
28084                 ]
28085             };
28086         }
28087         
28088         var cfg = {
28089             tag: 'div',
28090             cls: 'nav-tabs-custom',
28091             cn: [
28092                 h,
28093                 {
28094                     tag: 'div',
28095                     cls: 'tab-content no-padding',
28096                     cn: []
28097                 }
28098             ]
28099         };
28100
28101         return  cfg;
28102     },
28103     initEvents : function()
28104     {
28105         //Roo.log('add add pane handler');
28106         this.on('addpane', this.onAddPane, this);
28107     },
28108      /**
28109      * Updates the box title
28110      * @param {String} html to set the title to.
28111      */
28112     setTitle : function(value)
28113     {
28114         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28115     },
28116     onAddPane : function(pane)
28117     {
28118         this.panes.push(pane);
28119         //Roo.log('addpane');
28120         //Roo.log(pane);
28121         // tabs are rendere left to right..
28122         if(!this.showtabs){
28123             return;
28124         }
28125         
28126         var ctr = this.el.select('.nav-tabs', true).first();
28127          
28128          
28129         var existing = ctr.select('.nav-tab',true);
28130         var qty = existing.getCount();;
28131         
28132         
28133         var tab = ctr.createChild({
28134             tag : 'li',
28135             cls : 'nav-tab' + (qty ? '' : ' active'),
28136             cn : [
28137                 {
28138                     tag : 'a',
28139                     href:'#',
28140                     html : pane.title
28141                 }
28142             ]
28143         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28144         pane.tab = tab;
28145         
28146         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28147         if (!qty) {
28148             pane.el.addClass('active');
28149         }
28150         
28151                 
28152     },
28153     onTabClick : function(ev,un,ob,pane)
28154     {
28155         //Roo.log('tab - prev default');
28156         ev.preventDefault();
28157         
28158         
28159         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28160         pane.tab.addClass('active');
28161         //Roo.log(pane.title);
28162         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28163         // technically we should have a deactivate event.. but maybe add later.
28164         // and it should not de-activate the selected tab...
28165         this.fireEvent('activatepane', pane);
28166         pane.el.addClass('active');
28167         pane.fireEvent('activate');
28168         
28169         
28170     },
28171     
28172     getActivePane : function()
28173     {
28174         var r = false;
28175         Roo.each(this.panes, function(p) {
28176             if(p.el.hasClass('active')){
28177                 r = p;
28178                 return false;
28179             }
28180             
28181             return;
28182         });
28183         
28184         return r;
28185     }
28186     
28187     
28188 });
28189
28190  
28191 /*
28192  * - LGPL
28193  *
28194  * Tab pane
28195  * 
28196  */
28197 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28198 /**
28199  * @class Roo.bootstrap.TabPane
28200  * @extends Roo.bootstrap.Component
28201  * Bootstrap TabPane class
28202  * @cfg {Boolean} active (false | true) Default false
28203  * @cfg {String} title title of panel
28204
28205  * 
28206  * @constructor
28207  * Create a new TabPane
28208  * @param {Object} config The config object
28209  */
28210
28211 Roo.bootstrap.dash.TabPane = function(config){
28212     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28213     
28214     this.addEvents({
28215         // raw events
28216         /**
28217          * @event activate
28218          * When a pane is activated
28219          * @param {Roo.bootstrap.dash.TabPane} pane
28220          */
28221         "activate" : true
28222          
28223     });
28224 };
28225
28226 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28227     
28228     active : false,
28229     title : '',
28230     
28231     // the tabBox that this is attached to.
28232     tab : false,
28233      
28234     getAutoCreate : function() 
28235     {
28236         var cfg = {
28237             tag: 'div',
28238             cls: 'tab-pane'
28239         };
28240         
28241         if(this.active){
28242             cfg.cls += ' active';
28243         }
28244         
28245         return cfg;
28246     },
28247     initEvents  : function()
28248     {
28249         //Roo.log('trigger add pane handler');
28250         this.parent().fireEvent('addpane', this)
28251     },
28252     
28253      /**
28254      * Updates the tab title 
28255      * @param {String} html to set the title to.
28256      */
28257     setTitle: function(str)
28258     {
28259         if (!this.tab) {
28260             return;
28261         }
28262         this.title = str;
28263         this.tab.select('a', true).first().dom.innerHTML = str;
28264         
28265     }
28266     
28267     
28268     
28269 });
28270
28271  
28272
28273
28274  /*
28275  * - LGPL
28276  *
28277  * menu
28278  * 
28279  */
28280 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28281
28282 /**
28283  * @class Roo.bootstrap.menu.Menu
28284  * @extends Roo.bootstrap.Component
28285  * Bootstrap Menu class - container for Menu
28286  * @cfg {String} html Text of the menu
28287  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28288  * @cfg {String} icon Font awesome icon
28289  * @cfg {String} pos Menu align to (top | bottom) default bottom
28290  * 
28291  * 
28292  * @constructor
28293  * Create a new Menu
28294  * @param {Object} config The config object
28295  */
28296
28297
28298 Roo.bootstrap.menu.Menu = function(config){
28299     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28300     
28301     this.addEvents({
28302         /**
28303          * @event beforeshow
28304          * Fires before this menu is displayed
28305          * @param {Roo.bootstrap.menu.Menu} this
28306          */
28307         beforeshow : true,
28308         /**
28309          * @event beforehide
28310          * Fires before this menu is hidden
28311          * @param {Roo.bootstrap.menu.Menu} this
28312          */
28313         beforehide : true,
28314         /**
28315          * @event show
28316          * Fires after this menu is displayed
28317          * @param {Roo.bootstrap.menu.Menu} this
28318          */
28319         show : true,
28320         /**
28321          * @event hide
28322          * Fires after this menu is hidden
28323          * @param {Roo.bootstrap.menu.Menu} this
28324          */
28325         hide : true,
28326         /**
28327          * @event click
28328          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28329          * @param {Roo.bootstrap.menu.Menu} this
28330          * @param {Roo.EventObject} e
28331          */
28332         click : true
28333     });
28334     
28335 };
28336
28337 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28338     
28339     submenu : false,
28340     html : '',
28341     weight : 'default',
28342     icon : false,
28343     pos : 'bottom',
28344     
28345     
28346     getChildContainer : function() {
28347         if(this.isSubMenu){
28348             return this.el;
28349         }
28350         
28351         return this.el.select('ul.dropdown-menu', true).first();  
28352     },
28353     
28354     getAutoCreate : function()
28355     {
28356         var text = [
28357             {
28358                 tag : 'span',
28359                 cls : 'roo-menu-text',
28360                 html : this.html
28361             }
28362         ];
28363         
28364         if(this.icon){
28365             text.unshift({
28366                 tag : 'i',
28367                 cls : 'fa ' + this.icon
28368             })
28369         }
28370         
28371         
28372         var cfg = {
28373             tag : 'div',
28374             cls : 'btn-group',
28375             cn : [
28376                 {
28377                     tag : 'button',
28378                     cls : 'dropdown-button btn btn-' + this.weight,
28379                     cn : text
28380                 },
28381                 {
28382                     tag : 'button',
28383                     cls : 'dropdown-toggle btn btn-' + this.weight,
28384                     cn : [
28385                         {
28386                             tag : 'span',
28387                             cls : 'caret'
28388                         }
28389                     ]
28390                 },
28391                 {
28392                     tag : 'ul',
28393                     cls : 'dropdown-menu'
28394                 }
28395             ]
28396             
28397         };
28398         
28399         if(this.pos == 'top'){
28400             cfg.cls += ' dropup';
28401         }
28402         
28403         if(this.isSubMenu){
28404             cfg = {
28405                 tag : 'ul',
28406                 cls : 'dropdown-menu'
28407             }
28408         }
28409         
28410         return cfg;
28411     },
28412     
28413     onRender : function(ct, position)
28414     {
28415         this.isSubMenu = ct.hasClass('dropdown-submenu');
28416         
28417         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28418     },
28419     
28420     initEvents : function() 
28421     {
28422         if(this.isSubMenu){
28423             return;
28424         }
28425         
28426         this.hidden = true;
28427         
28428         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28429         this.triggerEl.on('click', this.onTriggerPress, this);
28430         
28431         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28432         this.buttonEl.on('click', this.onClick, this);
28433         
28434     },
28435     
28436     list : function()
28437     {
28438         if(this.isSubMenu){
28439             return this.el;
28440         }
28441         
28442         return this.el.select('ul.dropdown-menu', true).first();
28443     },
28444     
28445     onClick : function(e)
28446     {
28447         this.fireEvent("click", this, e);
28448     },
28449     
28450     onTriggerPress  : function(e)
28451     {   
28452         if (this.isVisible()) {
28453             this.hide();
28454         } else {
28455             this.show();
28456         }
28457     },
28458     
28459     isVisible : function(){
28460         return !this.hidden;
28461     },
28462     
28463     show : function()
28464     {
28465         this.fireEvent("beforeshow", this);
28466         
28467         this.hidden = false;
28468         this.el.addClass('open');
28469         
28470         Roo.get(document).on("mouseup", this.onMouseUp, this);
28471         
28472         this.fireEvent("show", this);
28473         
28474         
28475     },
28476     
28477     hide : function()
28478     {
28479         this.fireEvent("beforehide", this);
28480         
28481         this.hidden = true;
28482         this.el.removeClass('open');
28483         
28484         Roo.get(document).un("mouseup", this.onMouseUp);
28485         
28486         this.fireEvent("hide", this);
28487     },
28488     
28489     onMouseUp : function()
28490     {
28491         this.hide();
28492     }
28493     
28494 });
28495
28496  
28497  /*
28498  * - LGPL
28499  *
28500  * menu item
28501  * 
28502  */
28503 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28504
28505 /**
28506  * @class Roo.bootstrap.menu.Item
28507  * @extends Roo.bootstrap.Component
28508  * Bootstrap MenuItem class
28509  * @cfg {Boolean} submenu (true | false) default false
28510  * @cfg {String} html text of the item
28511  * @cfg {String} href the link
28512  * @cfg {Boolean} disable (true | false) default false
28513  * @cfg {Boolean} preventDefault (true | false) default true
28514  * @cfg {String} icon Font awesome icon
28515  * @cfg {String} pos Submenu align to (left | right) default right 
28516  * 
28517  * 
28518  * @constructor
28519  * Create a new Item
28520  * @param {Object} config The config object
28521  */
28522
28523
28524 Roo.bootstrap.menu.Item = function(config){
28525     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28526     this.addEvents({
28527         /**
28528          * @event mouseover
28529          * Fires when the mouse is hovering over this menu
28530          * @param {Roo.bootstrap.menu.Item} this
28531          * @param {Roo.EventObject} e
28532          */
28533         mouseover : true,
28534         /**
28535          * @event mouseout
28536          * Fires when the mouse exits this menu
28537          * @param {Roo.bootstrap.menu.Item} this
28538          * @param {Roo.EventObject} e
28539          */
28540         mouseout : true,
28541         // raw events
28542         /**
28543          * @event click
28544          * The raw click event for the entire grid.
28545          * @param {Roo.EventObject} e
28546          */
28547         click : true
28548     });
28549 };
28550
28551 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28552     
28553     submenu : false,
28554     href : '',
28555     html : '',
28556     preventDefault: true,
28557     disable : false,
28558     icon : false,
28559     pos : 'right',
28560     
28561     getAutoCreate : function()
28562     {
28563         var text = [
28564             {
28565                 tag : 'span',
28566                 cls : 'roo-menu-item-text',
28567                 html : this.html
28568             }
28569         ];
28570         
28571         if(this.icon){
28572             text.unshift({
28573                 tag : 'i',
28574                 cls : 'fa ' + this.icon
28575             })
28576         }
28577         
28578         var cfg = {
28579             tag : 'li',
28580             cn : [
28581                 {
28582                     tag : 'a',
28583                     href : this.href || '#',
28584                     cn : text
28585                 }
28586             ]
28587         };
28588         
28589         if(this.disable){
28590             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28591         }
28592         
28593         if(this.submenu){
28594             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28595             
28596             if(this.pos == 'left'){
28597                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28598             }
28599         }
28600         
28601         return cfg;
28602     },
28603     
28604     initEvents : function() 
28605     {
28606         this.el.on('mouseover', this.onMouseOver, this);
28607         this.el.on('mouseout', this.onMouseOut, this);
28608         
28609         this.el.select('a', true).first().on('click', this.onClick, this);
28610         
28611     },
28612     
28613     onClick : function(e)
28614     {
28615         if(this.preventDefault){
28616             e.preventDefault();
28617         }
28618         
28619         this.fireEvent("click", this, e);
28620     },
28621     
28622     onMouseOver : function(e)
28623     {
28624         if(this.submenu && this.pos == 'left'){
28625             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28626         }
28627         
28628         this.fireEvent("mouseover", this, e);
28629     },
28630     
28631     onMouseOut : function(e)
28632     {
28633         this.fireEvent("mouseout", this, e);
28634     }
28635 });
28636
28637  
28638
28639  /*
28640  * - LGPL
28641  *
28642  * menu separator
28643  * 
28644  */
28645 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28646
28647 /**
28648  * @class Roo.bootstrap.menu.Separator
28649  * @extends Roo.bootstrap.Component
28650  * Bootstrap Separator class
28651  * 
28652  * @constructor
28653  * Create a new Separator
28654  * @param {Object} config The config object
28655  */
28656
28657
28658 Roo.bootstrap.menu.Separator = function(config){
28659     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28660 };
28661
28662 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28663     
28664     getAutoCreate : function(){
28665         var cfg = {
28666             tag : 'li',
28667             cls: 'divider'
28668         };
28669         
28670         return cfg;
28671     }
28672    
28673 });
28674
28675  
28676
28677  /*
28678  * - LGPL
28679  *
28680  * Tooltip
28681  * 
28682  */
28683
28684 /**
28685  * @class Roo.bootstrap.Tooltip
28686  * Bootstrap Tooltip class
28687  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28688  * to determine which dom element triggers the tooltip.
28689  * 
28690  * It needs to add support for additional attributes like tooltip-position
28691  * 
28692  * @constructor
28693  * Create a new Toolti
28694  * @param {Object} config The config object
28695  */
28696
28697 Roo.bootstrap.Tooltip = function(config){
28698     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28699     
28700     this.alignment = Roo.bootstrap.Tooltip.alignment;
28701     
28702     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28703         this.alignment = config.alignment;
28704     }
28705     
28706 };
28707
28708 Roo.apply(Roo.bootstrap.Tooltip, {
28709     /**
28710      * @function init initialize tooltip monitoring.
28711      * @static
28712      */
28713     currentEl : false,
28714     currentTip : false,
28715     currentRegion : false,
28716     
28717     //  init : delay?
28718     
28719     init : function()
28720     {
28721         Roo.get(document).on('mouseover', this.enter ,this);
28722         Roo.get(document).on('mouseout', this.leave, this);
28723          
28724         
28725         this.currentTip = new Roo.bootstrap.Tooltip();
28726     },
28727     
28728     enter : function(ev)
28729     {
28730         var dom = ev.getTarget();
28731         
28732         //Roo.log(['enter',dom]);
28733         var el = Roo.fly(dom);
28734         if (this.currentEl) {
28735             //Roo.log(dom);
28736             //Roo.log(this.currentEl);
28737             //Roo.log(this.currentEl.contains(dom));
28738             if (this.currentEl == el) {
28739                 return;
28740             }
28741             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28742                 return;
28743             }
28744
28745         }
28746         
28747         if (this.currentTip.el) {
28748             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28749         }    
28750         //Roo.log(ev);
28751         
28752         if(!el || el.dom == document){
28753             return;
28754         }
28755         
28756         var bindEl = el;
28757         
28758         // you can not look for children, as if el is the body.. then everythign is the child..
28759         if (!el.attr('tooltip')) { //
28760             if (!el.select("[tooltip]").elements.length) {
28761                 return;
28762             }
28763             // is the mouse over this child...?
28764             bindEl = el.select("[tooltip]").first();
28765             var xy = ev.getXY();
28766             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28767                 //Roo.log("not in region.");
28768                 return;
28769             }
28770             //Roo.log("child element over..");
28771             
28772         }
28773         this.currentEl = bindEl;
28774         this.currentTip.bind(bindEl);
28775         this.currentRegion = Roo.lib.Region.getRegion(dom);
28776         this.currentTip.enter();
28777         
28778     },
28779     leave : function(ev)
28780     {
28781         var dom = ev.getTarget();
28782         //Roo.log(['leave',dom]);
28783         if (!this.currentEl) {
28784             return;
28785         }
28786         
28787         
28788         if (dom != this.currentEl.dom) {
28789             return;
28790         }
28791         var xy = ev.getXY();
28792         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28793             return;
28794         }
28795         // only activate leave if mouse cursor is outside... bounding box..
28796         
28797         
28798         
28799         
28800         if (this.currentTip) {
28801             this.currentTip.leave();
28802         }
28803         //Roo.log('clear currentEl');
28804         this.currentEl = false;
28805         
28806         
28807     },
28808     alignment : {
28809         'left' : ['r-l', [-2,0], 'right'],
28810         'right' : ['l-r', [2,0], 'left'],
28811         'bottom' : ['t-b', [0,2], 'top'],
28812         'top' : [ 'b-t', [0,-2], 'bottom']
28813     }
28814     
28815 });
28816
28817
28818 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28819     
28820     
28821     bindEl : false,
28822     
28823     delay : null, // can be { show : 300 , hide: 500}
28824     
28825     timeout : null,
28826     
28827     hoverState : null, //???
28828     
28829     placement : 'bottom', 
28830     
28831     alignment : false,
28832     
28833     getAutoCreate : function(){
28834     
28835         var cfg = {
28836            cls : 'tooltip',   
28837            role : 'tooltip',
28838            cn : [
28839                 {
28840                     cls : 'tooltip-arrow arrow'
28841                 },
28842                 {
28843                     cls : 'tooltip-inner'
28844                 }
28845            ]
28846         };
28847         
28848         return cfg;
28849     },
28850     bind : function(el)
28851     {
28852         this.bindEl = el;
28853     },
28854     
28855     initEvents : function()
28856     {
28857         this.arrowEl = this.el.select('.arrow', true).first();
28858         this.innerEl = this.el.select('.tooltip-inner', true).first();
28859     },
28860     
28861     enter : function () {
28862        
28863         if (this.timeout != null) {
28864             clearTimeout(this.timeout);
28865         }
28866         
28867         this.hoverState = 'in';
28868          //Roo.log("enter - show");
28869         if (!this.delay || !this.delay.show) {
28870             this.show();
28871             return;
28872         }
28873         var _t = this;
28874         this.timeout = setTimeout(function () {
28875             if (_t.hoverState == 'in') {
28876                 _t.show();
28877             }
28878         }, this.delay.show);
28879     },
28880     leave : function()
28881     {
28882         clearTimeout(this.timeout);
28883     
28884         this.hoverState = 'out';
28885          if (!this.delay || !this.delay.hide) {
28886             this.hide();
28887             return;
28888         }
28889        
28890         var _t = this;
28891         this.timeout = setTimeout(function () {
28892             //Roo.log("leave - timeout");
28893             
28894             if (_t.hoverState == 'out') {
28895                 _t.hide();
28896                 Roo.bootstrap.Tooltip.currentEl = false;
28897             }
28898         }, delay);
28899     },
28900     
28901     show : function (msg)
28902     {
28903         if (!this.el) {
28904             this.render(document.body);
28905         }
28906         // set content.
28907         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28908         
28909         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28910         
28911         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28912         
28913         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28914                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28915         
28916         var placement = typeof this.placement == 'function' ?
28917             this.placement.call(this, this.el, on_el) :
28918             this.placement;
28919             
28920         var autoToken = /\s?auto?\s?/i;
28921         var autoPlace = autoToken.test(placement);
28922         if (autoPlace) {
28923             placement = placement.replace(autoToken, '') || 'top';
28924         }
28925         
28926         //this.el.detach()
28927         //this.el.setXY([0,0]);
28928         this.el.show();
28929         //this.el.dom.style.display='block';
28930         
28931         //this.el.appendTo(on_el);
28932         
28933         var p = this.getPosition();
28934         var box = this.el.getBox();
28935         
28936         if (autoPlace) {
28937             // fixme..
28938         }
28939         
28940         var align = this.alignment[placement];
28941         
28942         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28943         
28944         if(placement == 'top' || placement == 'bottom'){
28945             if(xy[0] < 0){
28946                 placement = 'right';
28947             }
28948             
28949             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28950                 placement = 'left';
28951             }
28952             
28953             var scroll = Roo.select('body', true).first().getScroll();
28954             
28955             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28956                 placement = 'top';
28957             }
28958             
28959             align = this.alignment[placement];
28960             
28961             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28962             
28963         }
28964         
28965         this.el.alignTo(this.bindEl, align[0],align[1]);
28966         //var arrow = this.el.select('.arrow',true).first();
28967         //arrow.set(align[2], 
28968         
28969         this.el.addClass(placement);
28970         this.el.addClass("bs-tooltip-"+ placement);
28971         
28972         this.el.addClass('in fade show');
28973         
28974         this.hoverState = null;
28975         
28976         if (this.el.hasClass('fade')) {
28977             // fade it?
28978         }
28979         
28980         
28981         
28982         
28983         
28984     },
28985     hide : function()
28986     {
28987          
28988         if (!this.el) {
28989             return;
28990         }
28991         //this.el.setXY([0,0]);
28992         this.el.removeClass(['show', 'in']);
28993         //this.el.hide();
28994         
28995     }
28996     
28997 });
28998  
28999
29000  /*
29001  * - LGPL
29002  *
29003  * Location Picker
29004  * 
29005  */
29006
29007 /**
29008  * @class Roo.bootstrap.LocationPicker
29009  * @extends Roo.bootstrap.Component
29010  * Bootstrap LocationPicker class
29011  * @cfg {Number} latitude Position when init default 0
29012  * @cfg {Number} longitude Position when init default 0
29013  * @cfg {Number} zoom default 15
29014  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29015  * @cfg {Boolean} mapTypeControl default false
29016  * @cfg {Boolean} disableDoubleClickZoom default false
29017  * @cfg {Boolean} scrollwheel default true
29018  * @cfg {Boolean} streetViewControl default false
29019  * @cfg {Number} radius default 0
29020  * @cfg {String} locationName
29021  * @cfg {Boolean} draggable default true
29022  * @cfg {Boolean} enableAutocomplete default false
29023  * @cfg {Boolean} enableReverseGeocode default true
29024  * @cfg {String} markerTitle
29025  * 
29026  * @constructor
29027  * Create a new LocationPicker
29028  * @param {Object} config The config object
29029  */
29030
29031
29032 Roo.bootstrap.LocationPicker = function(config){
29033     
29034     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29035     
29036     this.addEvents({
29037         /**
29038          * @event initial
29039          * Fires when the picker initialized.
29040          * @param {Roo.bootstrap.LocationPicker} this
29041          * @param {Google Location} location
29042          */
29043         initial : true,
29044         /**
29045          * @event positionchanged
29046          * Fires when the picker position changed.
29047          * @param {Roo.bootstrap.LocationPicker} this
29048          * @param {Google Location} location
29049          */
29050         positionchanged : true,
29051         /**
29052          * @event resize
29053          * Fires when the map resize.
29054          * @param {Roo.bootstrap.LocationPicker} this
29055          */
29056         resize : true,
29057         /**
29058          * @event show
29059          * Fires when the map show.
29060          * @param {Roo.bootstrap.LocationPicker} this
29061          */
29062         show : true,
29063         /**
29064          * @event hide
29065          * Fires when the map hide.
29066          * @param {Roo.bootstrap.LocationPicker} this
29067          */
29068         hide : true,
29069         /**
29070          * @event mapClick
29071          * Fires when click the map.
29072          * @param {Roo.bootstrap.LocationPicker} this
29073          * @param {Map event} e
29074          */
29075         mapClick : true,
29076         /**
29077          * @event mapRightClick
29078          * Fires when right click the map.
29079          * @param {Roo.bootstrap.LocationPicker} this
29080          * @param {Map event} e
29081          */
29082         mapRightClick : true,
29083         /**
29084          * @event markerClick
29085          * Fires when click the marker.
29086          * @param {Roo.bootstrap.LocationPicker} this
29087          * @param {Map event} e
29088          */
29089         markerClick : true,
29090         /**
29091          * @event markerRightClick
29092          * Fires when right click the marker.
29093          * @param {Roo.bootstrap.LocationPicker} this
29094          * @param {Map event} e
29095          */
29096         markerRightClick : true,
29097         /**
29098          * @event OverlayViewDraw
29099          * Fires when OverlayView Draw
29100          * @param {Roo.bootstrap.LocationPicker} this
29101          */
29102         OverlayViewDraw : true,
29103         /**
29104          * @event OverlayViewOnAdd
29105          * Fires when OverlayView Draw
29106          * @param {Roo.bootstrap.LocationPicker} this
29107          */
29108         OverlayViewOnAdd : true,
29109         /**
29110          * @event OverlayViewOnRemove
29111          * Fires when OverlayView Draw
29112          * @param {Roo.bootstrap.LocationPicker} this
29113          */
29114         OverlayViewOnRemove : true,
29115         /**
29116          * @event OverlayViewShow
29117          * Fires when OverlayView Draw
29118          * @param {Roo.bootstrap.LocationPicker} this
29119          * @param {Pixel} cpx
29120          */
29121         OverlayViewShow : true,
29122         /**
29123          * @event OverlayViewHide
29124          * Fires when OverlayView Draw
29125          * @param {Roo.bootstrap.LocationPicker} this
29126          */
29127         OverlayViewHide : true,
29128         /**
29129          * @event loadexception
29130          * Fires when load google lib failed.
29131          * @param {Roo.bootstrap.LocationPicker} this
29132          */
29133         loadexception : true
29134     });
29135         
29136 };
29137
29138 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29139     
29140     gMapContext: false,
29141     
29142     latitude: 0,
29143     longitude: 0,
29144     zoom: 15,
29145     mapTypeId: false,
29146     mapTypeControl: false,
29147     disableDoubleClickZoom: false,
29148     scrollwheel: true,
29149     streetViewControl: false,
29150     radius: 0,
29151     locationName: '',
29152     draggable: true,
29153     enableAutocomplete: false,
29154     enableReverseGeocode: true,
29155     markerTitle: '',
29156     
29157     getAutoCreate: function()
29158     {
29159
29160         var cfg = {
29161             tag: 'div',
29162             cls: 'roo-location-picker'
29163         };
29164         
29165         return cfg
29166     },
29167     
29168     initEvents: function(ct, position)
29169     {       
29170         if(!this.el.getWidth() || this.isApplied()){
29171             return;
29172         }
29173         
29174         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29175         
29176         this.initial();
29177     },
29178     
29179     initial: function()
29180     {
29181         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29182             this.fireEvent('loadexception', this);
29183             return;
29184         }
29185         
29186         if(!this.mapTypeId){
29187             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29188         }
29189         
29190         this.gMapContext = this.GMapContext();
29191         
29192         this.initOverlayView();
29193         
29194         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29195         
29196         var _this = this;
29197                 
29198         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29199             _this.setPosition(_this.gMapContext.marker.position);
29200         });
29201         
29202         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29203             _this.fireEvent('mapClick', this, event);
29204             
29205         });
29206
29207         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29208             _this.fireEvent('mapRightClick', this, event);
29209             
29210         });
29211         
29212         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29213             _this.fireEvent('markerClick', this, event);
29214             
29215         });
29216
29217         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29218             _this.fireEvent('markerRightClick', this, event);
29219             
29220         });
29221         
29222         this.setPosition(this.gMapContext.location);
29223         
29224         this.fireEvent('initial', this, this.gMapContext.location);
29225     },
29226     
29227     initOverlayView: function()
29228     {
29229         var _this = this;
29230         
29231         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29232             
29233             draw: function()
29234             {
29235                 _this.fireEvent('OverlayViewDraw', _this);
29236             },
29237             
29238             onAdd: function()
29239             {
29240                 _this.fireEvent('OverlayViewOnAdd', _this);
29241             },
29242             
29243             onRemove: function()
29244             {
29245                 _this.fireEvent('OverlayViewOnRemove', _this);
29246             },
29247             
29248             show: function(cpx)
29249             {
29250                 _this.fireEvent('OverlayViewShow', _this, cpx);
29251             },
29252             
29253             hide: function()
29254             {
29255                 _this.fireEvent('OverlayViewHide', _this);
29256             }
29257             
29258         });
29259     },
29260     
29261     fromLatLngToContainerPixel: function(event)
29262     {
29263         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29264     },
29265     
29266     isApplied: function() 
29267     {
29268         return this.getGmapContext() == false ? false : true;
29269     },
29270     
29271     getGmapContext: function() 
29272     {
29273         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29274     },
29275     
29276     GMapContext: function() 
29277     {
29278         var position = new google.maps.LatLng(this.latitude, this.longitude);
29279         
29280         var _map = new google.maps.Map(this.el.dom, {
29281             center: position,
29282             zoom: this.zoom,
29283             mapTypeId: this.mapTypeId,
29284             mapTypeControl: this.mapTypeControl,
29285             disableDoubleClickZoom: this.disableDoubleClickZoom,
29286             scrollwheel: this.scrollwheel,
29287             streetViewControl: this.streetViewControl,
29288             locationName: this.locationName,
29289             draggable: this.draggable,
29290             enableAutocomplete: this.enableAutocomplete,
29291             enableReverseGeocode: this.enableReverseGeocode
29292         });
29293         
29294         var _marker = new google.maps.Marker({
29295             position: position,
29296             map: _map,
29297             title: this.markerTitle,
29298             draggable: this.draggable
29299         });
29300         
29301         return {
29302             map: _map,
29303             marker: _marker,
29304             circle: null,
29305             location: position,
29306             radius: this.radius,
29307             locationName: this.locationName,
29308             addressComponents: {
29309                 formatted_address: null,
29310                 addressLine1: null,
29311                 addressLine2: null,
29312                 streetName: null,
29313                 streetNumber: null,
29314                 city: null,
29315                 district: null,
29316                 state: null,
29317                 stateOrProvince: null
29318             },
29319             settings: this,
29320             domContainer: this.el.dom,
29321             geodecoder: new google.maps.Geocoder()
29322         };
29323     },
29324     
29325     drawCircle: function(center, radius, options) 
29326     {
29327         if (this.gMapContext.circle != null) {
29328             this.gMapContext.circle.setMap(null);
29329         }
29330         if (radius > 0) {
29331             radius *= 1;
29332             options = Roo.apply({}, options, {
29333                 strokeColor: "#0000FF",
29334                 strokeOpacity: .35,
29335                 strokeWeight: 2,
29336                 fillColor: "#0000FF",
29337                 fillOpacity: .2
29338             });
29339             
29340             options.map = this.gMapContext.map;
29341             options.radius = radius;
29342             options.center = center;
29343             this.gMapContext.circle = new google.maps.Circle(options);
29344             return this.gMapContext.circle;
29345         }
29346         
29347         return null;
29348     },
29349     
29350     setPosition: function(location) 
29351     {
29352         this.gMapContext.location = location;
29353         this.gMapContext.marker.setPosition(location);
29354         this.gMapContext.map.panTo(location);
29355         this.drawCircle(location, this.gMapContext.radius, {});
29356         
29357         var _this = this;
29358         
29359         if (this.gMapContext.settings.enableReverseGeocode) {
29360             this.gMapContext.geodecoder.geocode({
29361                 latLng: this.gMapContext.location
29362             }, function(results, status) {
29363                 
29364                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29365                     _this.gMapContext.locationName = results[0].formatted_address;
29366                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29367                     
29368                     _this.fireEvent('positionchanged', this, location);
29369                 }
29370             });
29371             
29372             return;
29373         }
29374         
29375         this.fireEvent('positionchanged', this, location);
29376     },
29377     
29378     resize: function()
29379     {
29380         google.maps.event.trigger(this.gMapContext.map, "resize");
29381         
29382         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29383         
29384         this.fireEvent('resize', this);
29385     },
29386     
29387     setPositionByLatLng: function(latitude, longitude)
29388     {
29389         this.setPosition(new google.maps.LatLng(latitude, longitude));
29390     },
29391     
29392     getCurrentPosition: function() 
29393     {
29394         return {
29395             latitude: this.gMapContext.location.lat(),
29396             longitude: this.gMapContext.location.lng()
29397         };
29398     },
29399     
29400     getAddressName: function() 
29401     {
29402         return this.gMapContext.locationName;
29403     },
29404     
29405     getAddressComponents: function() 
29406     {
29407         return this.gMapContext.addressComponents;
29408     },
29409     
29410     address_component_from_google_geocode: function(address_components) 
29411     {
29412         var result = {};
29413         
29414         for (var i = 0; i < address_components.length; i++) {
29415             var component = address_components[i];
29416             if (component.types.indexOf("postal_code") >= 0) {
29417                 result.postalCode = component.short_name;
29418             } else if (component.types.indexOf("street_number") >= 0) {
29419                 result.streetNumber = component.short_name;
29420             } else if (component.types.indexOf("route") >= 0) {
29421                 result.streetName = component.short_name;
29422             } else if (component.types.indexOf("neighborhood") >= 0) {
29423                 result.city = component.short_name;
29424             } else if (component.types.indexOf("locality") >= 0) {
29425                 result.city = component.short_name;
29426             } else if (component.types.indexOf("sublocality") >= 0) {
29427                 result.district = component.short_name;
29428             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29429                 result.stateOrProvince = component.short_name;
29430             } else if (component.types.indexOf("country") >= 0) {
29431                 result.country = component.short_name;
29432             }
29433         }
29434         
29435         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29436         result.addressLine2 = "";
29437         return result;
29438     },
29439     
29440     setZoomLevel: function(zoom)
29441     {
29442         this.gMapContext.map.setZoom(zoom);
29443     },
29444     
29445     show: function()
29446     {
29447         if(!this.el){
29448             return;
29449         }
29450         
29451         this.el.show();
29452         
29453         this.resize();
29454         
29455         this.fireEvent('show', this);
29456     },
29457     
29458     hide: function()
29459     {
29460         if(!this.el){
29461             return;
29462         }
29463         
29464         this.el.hide();
29465         
29466         this.fireEvent('hide', this);
29467     }
29468     
29469 });
29470
29471 Roo.apply(Roo.bootstrap.LocationPicker, {
29472     
29473     OverlayView : function(map, options)
29474     {
29475         options = options || {};
29476         
29477         this.setMap(map);
29478     }
29479     
29480     
29481 });/**
29482  * @class Roo.bootstrap.Alert
29483  * @extends Roo.bootstrap.Component
29484  * Bootstrap Alert class - shows an alert area box
29485  * eg
29486  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29487   Enter a valid email address
29488 </div>
29489  * @licence LGPL
29490  * @cfg {String} title The title of alert
29491  * @cfg {String} html The content of alert
29492  * @cfg {String} weight (  success | info | warning | danger )
29493  * @cfg {String} faicon font-awesomeicon
29494  * 
29495  * @constructor
29496  * Create a new alert
29497  * @param {Object} config The config object
29498  */
29499
29500
29501 Roo.bootstrap.Alert = function(config){
29502     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29503     
29504 };
29505
29506 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29507     
29508     title: '',
29509     html: '',
29510     weight: false,
29511     faicon: false,
29512     
29513     getAutoCreate : function()
29514     {
29515         
29516         var cfg = {
29517             tag : 'div',
29518             cls : 'alert',
29519             cn : [
29520                 {
29521                     tag : 'i',
29522                     cls : 'roo-alert-icon'
29523                     
29524                 },
29525                 {
29526                     tag : 'b',
29527                     cls : 'roo-alert-title',
29528                     html : this.title
29529                 },
29530                 {
29531                     tag : 'span',
29532                     cls : 'roo-alert-text',
29533                     html : this.html
29534                 }
29535             ]
29536         };
29537         
29538         if(this.faicon){
29539             cfg.cn[0].cls += ' fa ' + this.faicon;
29540         }
29541         
29542         if(this.weight){
29543             cfg.cls += ' alert-' + this.weight;
29544         }
29545         
29546         return cfg;
29547     },
29548     
29549     initEvents: function() 
29550     {
29551         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29552     },
29553     
29554     setTitle : function(str)
29555     {
29556         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29557     },
29558     
29559     setText : function(str)
29560     {
29561         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29562     },
29563     
29564     setWeight : function(weight)
29565     {
29566         if(this.weight){
29567             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29568         }
29569         
29570         this.weight = weight;
29571         
29572         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29573     },
29574     
29575     setIcon : function(icon)
29576     {
29577         if(this.faicon){
29578             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29579         }
29580         
29581         this.faicon = icon;
29582         
29583         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29584     },
29585     
29586     hide: function() 
29587     {
29588         this.el.hide();   
29589     },
29590     
29591     show: function() 
29592     {  
29593         this.el.show();   
29594     }
29595     
29596 });
29597
29598  
29599 /*
29600 * Licence: LGPL
29601 */
29602
29603 /**
29604  * @class Roo.bootstrap.UploadCropbox
29605  * @extends Roo.bootstrap.Component
29606  * Bootstrap UploadCropbox class
29607  * @cfg {String} emptyText show when image has been loaded
29608  * @cfg {String} rotateNotify show when image too small to rotate
29609  * @cfg {Number} errorTimeout default 3000
29610  * @cfg {Number} minWidth default 300
29611  * @cfg {Number} minHeight default 300
29612  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29613  * @cfg {Boolean} isDocument (true|false) default false
29614  * @cfg {String} url action url
29615  * @cfg {String} paramName default 'imageUpload'
29616  * @cfg {String} method default POST
29617  * @cfg {Boolean} loadMask (true|false) default true
29618  * @cfg {Boolean} loadingText default 'Loading...'
29619  * 
29620  * @constructor
29621  * Create a new UploadCropbox
29622  * @param {Object} config The config object
29623  */
29624
29625 Roo.bootstrap.UploadCropbox = function(config){
29626     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29627     
29628     this.addEvents({
29629         /**
29630          * @event beforeselectfile
29631          * Fire before select file
29632          * @param {Roo.bootstrap.UploadCropbox} this
29633          */
29634         "beforeselectfile" : true,
29635         /**
29636          * @event initial
29637          * Fire after initEvent
29638          * @param {Roo.bootstrap.UploadCropbox} this
29639          */
29640         "initial" : true,
29641         /**
29642          * @event crop
29643          * Fire after initEvent
29644          * @param {Roo.bootstrap.UploadCropbox} this
29645          * @param {String} data
29646          */
29647         "crop" : true,
29648         /**
29649          * @event prepare
29650          * Fire when preparing the file data
29651          * @param {Roo.bootstrap.UploadCropbox} this
29652          * @param {Object} file
29653          */
29654         "prepare" : true,
29655         /**
29656          * @event exception
29657          * Fire when get exception
29658          * @param {Roo.bootstrap.UploadCropbox} this
29659          * @param {XMLHttpRequest} xhr
29660          */
29661         "exception" : true,
29662         /**
29663          * @event beforeloadcanvas
29664          * Fire before load the canvas
29665          * @param {Roo.bootstrap.UploadCropbox} this
29666          * @param {String} src
29667          */
29668         "beforeloadcanvas" : true,
29669         /**
29670          * @event trash
29671          * Fire when trash image
29672          * @param {Roo.bootstrap.UploadCropbox} this
29673          */
29674         "trash" : true,
29675         /**
29676          * @event download
29677          * Fire when download the image
29678          * @param {Roo.bootstrap.UploadCropbox} this
29679          */
29680         "download" : true,
29681         /**
29682          * @event footerbuttonclick
29683          * Fire when footerbuttonclick
29684          * @param {Roo.bootstrap.UploadCropbox} this
29685          * @param {String} type
29686          */
29687         "footerbuttonclick" : true,
29688         /**
29689          * @event resize
29690          * Fire when resize
29691          * @param {Roo.bootstrap.UploadCropbox} this
29692          */
29693         "resize" : true,
29694         /**
29695          * @event rotate
29696          * Fire when rotate the image
29697          * @param {Roo.bootstrap.UploadCropbox} this
29698          * @param {String} pos
29699          */
29700         "rotate" : true,
29701         /**
29702          * @event inspect
29703          * Fire when inspect the file
29704          * @param {Roo.bootstrap.UploadCropbox} this
29705          * @param {Object} file
29706          */
29707         "inspect" : true,
29708         /**
29709          * @event upload
29710          * Fire when xhr upload the file
29711          * @param {Roo.bootstrap.UploadCropbox} this
29712          * @param {Object} data
29713          */
29714         "upload" : true,
29715         /**
29716          * @event arrange
29717          * Fire when arrange the file data
29718          * @param {Roo.bootstrap.UploadCropbox} this
29719          * @param {Object} formData
29720          */
29721         "arrange" : true
29722     });
29723     
29724     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29725 };
29726
29727 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29728     
29729     emptyText : 'Click to upload image',
29730     rotateNotify : 'Image is too small to rotate',
29731     errorTimeout : 3000,
29732     scale : 0,
29733     baseScale : 1,
29734     rotate : 0,
29735     dragable : false,
29736     pinching : false,
29737     mouseX : 0,
29738     mouseY : 0,
29739     cropData : false,
29740     minWidth : 300,
29741     minHeight : 300,
29742     file : false,
29743     exif : {},
29744     baseRotate : 1,
29745     cropType : 'image/jpeg',
29746     buttons : false,
29747     canvasLoaded : false,
29748     isDocument : false,
29749     method : 'POST',
29750     paramName : 'imageUpload',
29751     loadMask : true,
29752     loadingText : 'Loading...',
29753     maskEl : false,
29754     
29755     getAutoCreate : function()
29756     {
29757         var cfg = {
29758             tag : 'div',
29759             cls : 'roo-upload-cropbox',
29760             cn : [
29761                 {
29762                     tag : 'input',
29763                     cls : 'roo-upload-cropbox-selector',
29764                     type : 'file'
29765                 },
29766                 {
29767                     tag : 'div',
29768                     cls : 'roo-upload-cropbox-body',
29769                     style : 'cursor:pointer',
29770                     cn : [
29771                         {
29772                             tag : 'div',
29773                             cls : 'roo-upload-cropbox-preview'
29774                         },
29775                         {
29776                             tag : 'div',
29777                             cls : 'roo-upload-cropbox-thumb'
29778                         },
29779                         {
29780                             tag : 'div',
29781                             cls : 'roo-upload-cropbox-empty-notify',
29782                             html : this.emptyText
29783                         },
29784                         {
29785                             tag : 'div',
29786                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29787                             html : this.rotateNotify
29788                         }
29789                     ]
29790                 },
29791                 {
29792                     tag : 'div',
29793                     cls : 'roo-upload-cropbox-footer',
29794                     cn : {
29795                         tag : 'div',
29796                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29797                         cn : []
29798                     }
29799                 }
29800             ]
29801         };
29802         
29803         return cfg;
29804     },
29805     
29806     onRender : function(ct, position)
29807     {
29808         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29809         
29810         if (this.buttons.length) {
29811             
29812             Roo.each(this.buttons, function(bb) {
29813                 
29814                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29815                 
29816                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29817                 
29818             }, this);
29819         }
29820         
29821         if(this.loadMask){
29822             this.maskEl = this.el;
29823         }
29824     },
29825     
29826     initEvents : function()
29827     {
29828         this.urlAPI = (window.createObjectURL && window) || 
29829                                 (window.URL && URL.revokeObjectURL && URL) || 
29830                                 (window.webkitURL && webkitURL);
29831                         
29832         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29833         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29834         
29835         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29836         this.selectorEl.hide();
29837         
29838         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29839         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29840         
29841         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29842         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29843         this.thumbEl.hide();
29844         
29845         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29846         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29847         
29848         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29849         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29850         this.errorEl.hide();
29851         
29852         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29853         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29854         this.footerEl.hide();
29855         
29856         this.setThumbBoxSize();
29857         
29858         this.bind();
29859         
29860         this.resize();
29861         
29862         this.fireEvent('initial', this);
29863     },
29864
29865     bind : function()
29866     {
29867         var _this = this;
29868         
29869         window.addEventListener("resize", function() { _this.resize(); } );
29870         
29871         this.bodyEl.on('click', this.beforeSelectFile, this);
29872         
29873         if(Roo.isTouch){
29874             this.bodyEl.on('touchstart', this.onTouchStart, this);
29875             this.bodyEl.on('touchmove', this.onTouchMove, this);
29876             this.bodyEl.on('touchend', this.onTouchEnd, this);
29877         }
29878         
29879         if(!Roo.isTouch){
29880             this.bodyEl.on('mousedown', this.onMouseDown, this);
29881             this.bodyEl.on('mousemove', this.onMouseMove, this);
29882             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29883             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29884             Roo.get(document).on('mouseup', this.onMouseUp, this);
29885         }
29886         
29887         this.selectorEl.on('change', this.onFileSelected, this);
29888     },
29889     
29890     reset : function()
29891     {    
29892         this.scale = 0;
29893         this.baseScale = 1;
29894         this.rotate = 0;
29895         this.baseRotate = 1;
29896         this.dragable = false;
29897         this.pinching = false;
29898         this.mouseX = 0;
29899         this.mouseY = 0;
29900         this.cropData = false;
29901         this.notifyEl.dom.innerHTML = this.emptyText;
29902         
29903         this.selectorEl.dom.value = '';
29904         
29905     },
29906     
29907     resize : function()
29908     {
29909         if(this.fireEvent('resize', this) != false){
29910             this.setThumbBoxPosition();
29911             this.setCanvasPosition();
29912         }
29913     },
29914     
29915     onFooterButtonClick : function(e, el, o, type)
29916     {
29917         switch (type) {
29918             case 'rotate-left' :
29919                 this.onRotateLeft(e);
29920                 break;
29921             case 'rotate-right' :
29922                 this.onRotateRight(e);
29923                 break;
29924             case 'picture' :
29925                 this.beforeSelectFile(e);
29926                 break;
29927             case 'trash' :
29928                 this.trash(e);
29929                 break;
29930             case 'crop' :
29931                 this.crop(e);
29932                 break;
29933             case 'download' :
29934                 this.download(e);
29935                 break;
29936             default :
29937                 break;
29938         }
29939         
29940         this.fireEvent('footerbuttonclick', this, type);
29941     },
29942     
29943     beforeSelectFile : function(e)
29944     {
29945         e.preventDefault();
29946         
29947         if(this.fireEvent('beforeselectfile', this) != false){
29948             this.selectorEl.dom.click();
29949         }
29950     },
29951     
29952     onFileSelected : function(e)
29953     {
29954         e.preventDefault();
29955         
29956         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29957             return;
29958         }
29959         
29960         var file = this.selectorEl.dom.files[0];
29961         
29962         if(this.fireEvent('inspect', this, file) != false){
29963             this.prepare(file);
29964         }
29965         
29966     },
29967     
29968     trash : function(e)
29969     {
29970         this.fireEvent('trash', this);
29971     },
29972     
29973     download : function(e)
29974     {
29975         this.fireEvent('download', this);
29976     },
29977     
29978     loadCanvas : function(src)
29979     {   
29980         if(this.fireEvent('beforeloadcanvas', this, src) != false){
29981             
29982             this.reset();
29983             
29984             this.imageEl = document.createElement('img');
29985             
29986             var _this = this;
29987             
29988             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29989             
29990             this.imageEl.src = src;
29991         }
29992     },
29993     
29994     onLoadCanvas : function()
29995     {   
29996         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29997         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29998         
29999         this.bodyEl.un('click', this.beforeSelectFile, this);
30000         
30001         this.notifyEl.hide();
30002         this.thumbEl.show();
30003         this.footerEl.show();
30004         
30005         this.baseRotateLevel();
30006         
30007         if(this.isDocument){
30008             this.setThumbBoxSize();
30009         }
30010         
30011         this.setThumbBoxPosition();
30012         
30013         this.baseScaleLevel();
30014         
30015         this.draw();
30016         
30017         this.resize();
30018         
30019         this.canvasLoaded = true;
30020         
30021         if(this.loadMask){
30022             this.maskEl.unmask();
30023         }
30024         
30025     },
30026     
30027     setCanvasPosition : function()
30028     {   
30029         if(!this.canvasEl){
30030             return;
30031         }
30032         
30033         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30034         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30035         
30036         this.previewEl.setLeft(pw);
30037         this.previewEl.setTop(ph);
30038         
30039     },
30040     
30041     onMouseDown : function(e)
30042     {   
30043         e.stopEvent();
30044         
30045         this.dragable = true;
30046         this.pinching = false;
30047         
30048         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30049             this.dragable = false;
30050             return;
30051         }
30052         
30053         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30054         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30055         
30056     },
30057     
30058     onMouseMove : function(e)
30059     {   
30060         e.stopEvent();
30061         
30062         if(!this.canvasLoaded){
30063             return;
30064         }
30065         
30066         if (!this.dragable){
30067             return;
30068         }
30069         
30070         var minX = Math.ceil(this.thumbEl.getLeft(true));
30071         var minY = Math.ceil(this.thumbEl.getTop(true));
30072         
30073         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30074         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30075         
30076         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30077         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30078         
30079         x = x - this.mouseX;
30080         y = y - this.mouseY;
30081         
30082         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30083         var bgY = Math.ceil(y + this.previewEl.getTop(true));
30084         
30085         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30086         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30087         
30088         this.previewEl.setLeft(bgX);
30089         this.previewEl.setTop(bgY);
30090         
30091         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30092         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30093     },
30094     
30095     onMouseUp : function(e)
30096     {   
30097         e.stopEvent();
30098         
30099         this.dragable = false;
30100     },
30101     
30102     onMouseWheel : function(e)
30103     {   
30104         e.stopEvent();
30105         
30106         this.startScale = this.scale;
30107         
30108         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30109         
30110         if(!this.zoomable()){
30111             this.scale = this.startScale;
30112             return;
30113         }
30114         
30115         this.draw();
30116         
30117         return;
30118     },
30119     
30120     zoomable : function()
30121     {
30122         var minScale = this.thumbEl.getWidth() / this.minWidth;
30123         
30124         if(this.minWidth < this.minHeight){
30125             minScale = this.thumbEl.getHeight() / this.minHeight;
30126         }
30127         
30128         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30129         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30130         
30131         if(
30132                 this.isDocument &&
30133                 (this.rotate == 0 || this.rotate == 180) && 
30134                 (
30135                     width > this.imageEl.OriginWidth || 
30136                     height > this.imageEl.OriginHeight ||
30137                     (width < this.minWidth && height < this.minHeight)
30138                 )
30139         ){
30140             return false;
30141         }
30142         
30143         if(
30144                 this.isDocument &&
30145                 (this.rotate == 90 || this.rotate == 270) && 
30146                 (
30147                     width > this.imageEl.OriginWidth || 
30148                     height > this.imageEl.OriginHeight ||
30149                     (width < this.minHeight && height < this.minWidth)
30150                 )
30151         ){
30152             return false;
30153         }
30154         
30155         if(
30156                 !this.isDocument &&
30157                 (this.rotate == 0 || this.rotate == 180) && 
30158                 (
30159                     width < this.minWidth || 
30160                     width > this.imageEl.OriginWidth || 
30161                     height < this.minHeight || 
30162                     height > this.imageEl.OriginHeight
30163                 )
30164         ){
30165             return false;
30166         }
30167         
30168         if(
30169                 !this.isDocument &&
30170                 (this.rotate == 90 || this.rotate == 270) && 
30171                 (
30172                     width < this.minHeight || 
30173                     width > this.imageEl.OriginWidth || 
30174                     height < this.minWidth || 
30175                     height > this.imageEl.OriginHeight
30176                 )
30177         ){
30178             return false;
30179         }
30180         
30181         return true;
30182         
30183     },
30184     
30185     onRotateLeft : function(e)
30186     {   
30187         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30188             
30189             var minScale = this.thumbEl.getWidth() / this.minWidth;
30190             
30191             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30192             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30193             
30194             this.startScale = this.scale;
30195             
30196             while (this.getScaleLevel() < minScale){
30197             
30198                 this.scale = this.scale + 1;
30199                 
30200                 if(!this.zoomable()){
30201                     break;
30202                 }
30203                 
30204                 if(
30205                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30206                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30207                 ){
30208                     continue;
30209                 }
30210                 
30211                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30212
30213                 this.draw();
30214                 
30215                 return;
30216             }
30217             
30218             this.scale = this.startScale;
30219             
30220             this.onRotateFail();
30221             
30222             return false;
30223         }
30224         
30225         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30226
30227         if(this.isDocument){
30228             this.setThumbBoxSize();
30229             this.setThumbBoxPosition();
30230             this.setCanvasPosition();
30231         }
30232         
30233         this.draw();
30234         
30235         this.fireEvent('rotate', this, 'left');
30236         
30237     },
30238     
30239     onRotateRight : function(e)
30240     {
30241         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30242             
30243             var minScale = this.thumbEl.getWidth() / this.minWidth;
30244         
30245             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30246             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30247             
30248             this.startScale = this.scale;
30249             
30250             while (this.getScaleLevel() < minScale){
30251             
30252                 this.scale = this.scale + 1;
30253                 
30254                 if(!this.zoomable()){
30255                     break;
30256                 }
30257                 
30258                 if(
30259                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30260                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30261                 ){
30262                     continue;
30263                 }
30264                 
30265                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30266
30267                 this.draw();
30268                 
30269                 return;
30270             }
30271             
30272             this.scale = this.startScale;
30273             
30274             this.onRotateFail();
30275             
30276             return false;
30277         }
30278         
30279         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30280
30281         if(this.isDocument){
30282             this.setThumbBoxSize();
30283             this.setThumbBoxPosition();
30284             this.setCanvasPosition();
30285         }
30286         
30287         this.draw();
30288         
30289         this.fireEvent('rotate', this, 'right');
30290     },
30291     
30292     onRotateFail : function()
30293     {
30294         this.errorEl.show(true);
30295         
30296         var _this = this;
30297         
30298         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30299     },
30300     
30301     draw : function()
30302     {
30303         this.previewEl.dom.innerHTML = '';
30304         
30305         var canvasEl = document.createElement("canvas");
30306         
30307         var contextEl = canvasEl.getContext("2d");
30308         
30309         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30310         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30311         var center = this.imageEl.OriginWidth / 2;
30312         
30313         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30314             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30315             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30316             center = this.imageEl.OriginHeight / 2;
30317         }
30318         
30319         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30320         
30321         contextEl.translate(center, center);
30322         contextEl.rotate(this.rotate * Math.PI / 180);
30323
30324         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30325         
30326         this.canvasEl = document.createElement("canvas");
30327         
30328         this.contextEl = this.canvasEl.getContext("2d");
30329         
30330         switch (this.rotate) {
30331             case 0 :
30332                 
30333                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30334                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30335                 
30336                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30337                 
30338                 break;
30339             case 90 : 
30340                 
30341                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30342                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30343                 
30344                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30345                     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);
30346                     break;
30347                 }
30348                 
30349                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30350                 
30351                 break;
30352             case 180 :
30353                 
30354                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30355                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30356                 
30357                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30358                     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);
30359                     break;
30360                 }
30361                 
30362                 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);
30363                 
30364                 break;
30365             case 270 :
30366                 
30367                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30368                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30369         
30370                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30371                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30372                     break;
30373                 }
30374                 
30375                 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);
30376                 
30377                 break;
30378             default : 
30379                 break;
30380         }
30381         
30382         this.previewEl.appendChild(this.canvasEl);
30383         
30384         this.setCanvasPosition();
30385     },
30386     
30387     crop : function()
30388     {
30389         if(!this.canvasLoaded){
30390             return;
30391         }
30392         
30393         var imageCanvas = document.createElement("canvas");
30394         
30395         var imageContext = imageCanvas.getContext("2d");
30396         
30397         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30398         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30399         
30400         var center = imageCanvas.width / 2;
30401         
30402         imageContext.translate(center, center);
30403         
30404         imageContext.rotate(this.rotate * Math.PI / 180);
30405         
30406         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30407         
30408         var canvas = document.createElement("canvas");
30409         
30410         var context = canvas.getContext("2d");
30411                 
30412         canvas.width = this.minWidth;
30413         canvas.height = this.minHeight;
30414
30415         switch (this.rotate) {
30416             case 0 :
30417                 
30418                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30419                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30420                 
30421                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30422                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30423                 
30424                 var targetWidth = this.minWidth - 2 * x;
30425                 var targetHeight = this.minHeight - 2 * y;
30426                 
30427                 var scale = 1;
30428                 
30429                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30430                     scale = targetWidth / width;
30431                 }
30432                 
30433                 if(x > 0 && y == 0){
30434                     scale = targetHeight / height;
30435                 }
30436                 
30437                 if(x > 0 && y > 0){
30438                     scale = targetWidth / width;
30439                     
30440                     if(width < height){
30441                         scale = targetHeight / height;
30442                     }
30443                 }
30444                 
30445                 context.scale(scale, scale);
30446                 
30447                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30448                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30449
30450                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30451                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30452
30453                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30454                 
30455                 break;
30456             case 90 : 
30457                 
30458                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30459                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30460                 
30461                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30462                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30463                 
30464                 var targetWidth = this.minWidth - 2 * x;
30465                 var targetHeight = this.minHeight - 2 * y;
30466                 
30467                 var scale = 1;
30468                 
30469                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30470                     scale = targetWidth / width;
30471                 }
30472                 
30473                 if(x > 0 && y == 0){
30474                     scale = targetHeight / height;
30475                 }
30476                 
30477                 if(x > 0 && y > 0){
30478                     scale = targetWidth / width;
30479                     
30480                     if(width < height){
30481                         scale = targetHeight / height;
30482                     }
30483                 }
30484                 
30485                 context.scale(scale, scale);
30486                 
30487                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30488                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30489
30490                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30491                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30492                 
30493                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30494                 
30495                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30496                 
30497                 break;
30498             case 180 :
30499                 
30500                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30501                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30502                 
30503                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30504                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30505                 
30506                 var targetWidth = this.minWidth - 2 * x;
30507                 var targetHeight = this.minHeight - 2 * y;
30508                 
30509                 var scale = 1;
30510                 
30511                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30512                     scale = targetWidth / width;
30513                 }
30514                 
30515                 if(x > 0 && y == 0){
30516                     scale = targetHeight / height;
30517                 }
30518                 
30519                 if(x > 0 && y > 0){
30520                     scale = targetWidth / width;
30521                     
30522                     if(width < height){
30523                         scale = targetHeight / height;
30524                     }
30525                 }
30526                 
30527                 context.scale(scale, scale);
30528                 
30529                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30530                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30531
30532                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30533                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30534
30535                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30536                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30537                 
30538                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30539                 
30540                 break;
30541             case 270 :
30542                 
30543                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30544                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30545                 
30546                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30547                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30548                 
30549                 var targetWidth = this.minWidth - 2 * x;
30550                 var targetHeight = this.minHeight - 2 * y;
30551                 
30552                 var scale = 1;
30553                 
30554                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30555                     scale = targetWidth / width;
30556                 }
30557                 
30558                 if(x > 0 && y == 0){
30559                     scale = targetHeight / height;
30560                 }
30561                 
30562                 if(x > 0 && y > 0){
30563                     scale = targetWidth / width;
30564                     
30565                     if(width < height){
30566                         scale = targetHeight / height;
30567                     }
30568                 }
30569                 
30570                 context.scale(scale, scale);
30571                 
30572                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30573                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30574
30575                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30576                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30577                 
30578                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30579                 
30580                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30581                 
30582                 break;
30583             default : 
30584                 break;
30585         }
30586         
30587         this.cropData = canvas.toDataURL(this.cropType);
30588         
30589         if(this.fireEvent('crop', this, this.cropData) !== false){
30590             this.process(this.file, this.cropData);
30591         }
30592         
30593         return;
30594         
30595     },
30596     
30597     setThumbBoxSize : function()
30598     {
30599         var width, height;
30600         
30601         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30602             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30603             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30604             
30605             this.minWidth = width;
30606             this.minHeight = height;
30607             
30608             if(this.rotate == 90 || this.rotate == 270){
30609                 this.minWidth = height;
30610                 this.minHeight = width;
30611             }
30612         }
30613         
30614         height = 300;
30615         width = Math.ceil(this.minWidth * height / this.minHeight);
30616         
30617         if(this.minWidth > this.minHeight){
30618             width = 300;
30619             height = Math.ceil(this.minHeight * width / this.minWidth);
30620         }
30621         
30622         this.thumbEl.setStyle({
30623             width : width + 'px',
30624             height : height + 'px'
30625         });
30626
30627         return;
30628             
30629     },
30630     
30631     setThumbBoxPosition : function()
30632     {
30633         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30634         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30635         
30636         this.thumbEl.setLeft(x);
30637         this.thumbEl.setTop(y);
30638         
30639     },
30640     
30641     baseRotateLevel : function()
30642     {
30643         this.baseRotate = 1;
30644         
30645         if(
30646                 typeof(this.exif) != 'undefined' &&
30647                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30648                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30649         ){
30650             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30651         }
30652         
30653         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30654         
30655     },
30656     
30657     baseScaleLevel : function()
30658     {
30659         var width, height;
30660         
30661         if(this.isDocument){
30662             
30663             if(this.baseRotate == 6 || this.baseRotate == 8){
30664             
30665                 height = this.thumbEl.getHeight();
30666                 this.baseScale = height / this.imageEl.OriginWidth;
30667
30668                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30669                     width = this.thumbEl.getWidth();
30670                     this.baseScale = width / this.imageEl.OriginHeight;
30671                 }
30672
30673                 return;
30674             }
30675
30676             height = this.thumbEl.getHeight();
30677             this.baseScale = height / this.imageEl.OriginHeight;
30678
30679             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30680                 width = this.thumbEl.getWidth();
30681                 this.baseScale = width / this.imageEl.OriginWidth;
30682             }
30683
30684             return;
30685         }
30686         
30687         if(this.baseRotate == 6 || this.baseRotate == 8){
30688             
30689             width = this.thumbEl.getHeight();
30690             this.baseScale = width / this.imageEl.OriginHeight;
30691             
30692             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30693                 height = this.thumbEl.getWidth();
30694                 this.baseScale = height / this.imageEl.OriginHeight;
30695             }
30696             
30697             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30698                 height = this.thumbEl.getWidth();
30699                 this.baseScale = height / this.imageEl.OriginHeight;
30700                 
30701                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30702                     width = this.thumbEl.getHeight();
30703                     this.baseScale = width / this.imageEl.OriginWidth;
30704                 }
30705             }
30706             
30707             return;
30708         }
30709         
30710         width = this.thumbEl.getWidth();
30711         this.baseScale = width / this.imageEl.OriginWidth;
30712         
30713         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30714             height = this.thumbEl.getHeight();
30715             this.baseScale = height / this.imageEl.OriginHeight;
30716         }
30717         
30718         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30719             
30720             height = this.thumbEl.getHeight();
30721             this.baseScale = height / this.imageEl.OriginHeight;
30722             
30723             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30724                 width = this.thumbEl.getWidth();
30725                 this.baseScale = width / this.imageEl.OriginWidth;
30726             }
30727             
30728         }
30729         
30730         return;
30731     },
30732     
30733     getScaleLevel : function()
30734     {
30735         return this.baseScale * Math.pow(1.1, this.scale);
30736     },
30737     
30738     onTouchStart : function(e)
30739     {
30740         if(!this.canvasLoaded){
30741             this.beforeSelectFile(e);
30742             return;
30743         }
30744         
30745         var touches = e.browserEvent.touches;
30746         
30747         if(!touches){
30748             return;
30749         }
30750         
30751         if(touches.length == 1){
30752             this.onMouseDown(e);
30753             return;
30754         }
30755         
30756         if(touches.length != 2){
30757             return;
30758         }
30759         
30760         var coords = [];
30761         
30762         for(var i = 0, finger; finger = touches[i]; i++){
30763             coords.push(finger.pageX, finger.pageY);
30764         }
30765         
30766         var x = Math.pow(coords[0] - coords[2], 2);
30767         var y = Math.pow(coords[1] - coords[3], 2);
30768         
30769         this.startDistance = Math.sqrt(x + y);
30770         
30771         this.startScale = this.scale;
30772         
30773         this.pinching = true;
30774         this.dragable = false;
30775         
30776     },
30777     
30778     onTouchMove : function(e)
30779     {
30780         if(!this.pinching && !this.dragable){
30781             return;
30782         }
30783         
30784         var touches = e.browserEvent.touches;
30785         
30786         if(!touches){
30787             return;
30788         }
30789         
30790         if(this.dragable){
30791             this.onMouseMove(e);
30792             return;
30793         }
30794         
30795         var coords = [];
30796         
30797         for(var i = 0, finger; finger = touches[i]; i++){
30798             coords.push(finger.pageX, finger.pageY);
30799         }
30800         
30801         var x = Math.pow(coords[0] - coords[2], 2);
30802         var y = Math.pow(coords[1] - coords[3], 2);
30803         
30804         this.endDistance = Math.sqrt(x + y);
30805         
30806         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30807         
30808         if(!this.zoomable()){
30809             this.scale = this.startScale;
30810             return;
30811         }
30812         
30813         this.draw();
30814         
30815     },
30816     
30817     onTouchEnd : function(e)
30818     {
30819         this.pinching = false;
30820         this.dragable = false;
30821         
30822     },
30823     
30824     process : function(file, crop)
30825     {
30826         if(this.loadMask){
30827             this.maskEl.mask(this.loadingText);
30828         }
30829         
30830         this.xhr = new XMLHttpRequest();
30831         
30832         file.xhr = this.xhr;
30833
30834         this.xhr.open(this.method, this.url, true);
30835         
30836         var headers = {
30837             "Accept": "application/json",
30838             "Cache-Control": "no-cache",
30839             "X-Requested-With": "XMLHttpRequest"
30840         };
30841         
30842         for (var headerName in headers) {
30843             var headerValue = headers[headerName];
30844             if (headerValue) {
30845                 this.xhr.setRequestHeader(headerName, headerValue);
30846             }
30847         }
30848         
30849         var _this = this;
30850         
30851         this.xhr.onload = function()
30852         {
30853             _this.xhrOnLoad(_this.xhr);
30854         }
30855         
30856         this.xhr.onerror = function()
30857         {
30858             _this.xhrOnError(_this.xhr);
30859         }
30860         
30861         var formData = new FormData();
30862
30863         formData.append('returnHTML', 'NO');
30864         
30865         if(crop){
30866             formData.append('crop', crop);
30867         }
30868         
30869         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30870             formData.append(this.paramName, file, file.name);
30871         }
30872         
30873         if(typeof(file.filename) != 'undefined'){
30874             formData.append('filename', file.filename);
30875         }
30876         
30877         if(typeof(file.mimetype) != 'undefined'){
30878             formData.append('mimetype', file.mimetype);
30879         }
30880         
30881         if(this.fireEvent('arrange', this, formData) != false){
30882             this.xhr.send(formData);
30883         };
30884     },
30885     
30886     xhrOnLoad : function(xhr)
30887     {
30888         if(this.loadMask){
30889             this.maskEl.unmask();
30890         }
30891         
30892         if (xhr.readyState !== 4) {
30893             this.fireEvent('exception', this, xhr);
30894             return;
30895         }
30896
30897         var response = Roo.decode(xhr.responseText);
30898         
30899         if(!response.success){
30900             this.fireEvent('exception', this, xhr);
30901             return;
30902         }
30903         
30904         var response = Roo.decode(xhr.responseText);
30905         
30906         this.fireEvent('upload', this, response);
30907         
30908     },
30909     
30910     xhrOnError : function()
30911     {
30912         if(this.loadMask){
30913             this.maskEl.unmask();
30914         }
30915         
30916         Roo.log('xhr on error');
30917         
30918         var response = Roo.decode(xhr.responseText);
30919           
30920         Roo.log(response);
30921         
30922     },
30923     
30924     prepare : function(file)
30925     {   
30926         if(this.loadMask){
30927             this.maskEl.mask(this.loadingText);
30928         }
30929         
30930         this.file = false;
30931         this.exif = {};
30932         
30933         if(typeof(file) === 'string'){
30934             this.loadCanvas(file);
30935             return;
30936         }
30937         
30938         if(!file || !this.urlAPI){
30939             return;
30940         }
30941         
30942         this.file = file;
30943         this.cropType = file.type;
30944         
30945         var _this = this;
30946         
30947         if(this.fireEvent('prepare', this, this.file) != false){
30948             
30949             var reader = new FileReader();
30950             
30951             reader.onload = function (e) {
30952                 if (e.target.error) {
30953                     Roo.log(e.target.error);
30954                     return;
30955                 }
30956                 
30957                 var buffer = e.target.result,
30958                     dataView = new DataView(buffer),
30959                     offset = 2,
30960                     maxOffset = dataView.byteLength - 4,
30961                     markerBytes,
30962                     markerLength;
30963                 
30964                 if (dataView.getUint16(0) === 0xffd8) {
30965                     while (offset < maxOffset) {
30966                         markerBytes = dataView.getUint16(offset);
30967                         
30968                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30969                             markerLength = dataView.getUint16(offset + 2) + 2;
30970                             if (offset + markerLength > dataView.byteLength) {
30971                                 Roo.log('Invalid meta data: Invalid segment size.');
30972                                 break;
30973                             }
30974                             
30975                             if(markerBytes == 0xffe1){
30976                                 _this.parseExifData(
30977                                     dataView,
30978                                     offset,
30979                                     markerLength
30980                                 );
30981                             }
30982                             
30983                             offset += markerLength;
30984                             
30985                             continue;
30986                         }
30987                         
30988                         break;
30989                     }
30990                     
30991                 }
30992                 
30993                 var url = _this.urlAPI.createObjectURL(_this.file);
30994                 
30995                 _this.loadCanvas(url);
30996                 
30997                 return;
30998             }
30999             
31000             reader.readAsArrayBuffer(this.file);
31001             
31002         }
31003         
31004     },
31005     
31006     parseExifData : function(dataView, offset, length)
31007     {
31008         var tiffOffset = offset + 10,
31009             littleEndian,
31010             dirOffset;
31011     
31012         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31013             // No Exif data, might be XMP data instead
31014             return;
31015         }
31016         
31017         // Check for the ASCII code for "Exif" (0x45786966):
31018         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31019             // No Exif data, might be XMP data instead
31020             return;
31021         }
31022         if (tiffOffset + 8 > dataView.byteLength) {
31023             Roo.log('Invalid Exif data: Invalid segment size.');
31024             return;
31025         }
31026         // Check for the two null bytes:
31027         if (dataView.getUint16(offset + 8) !== 0x0000) {
31028             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31029             return;
31030         }
31031         // Check the byte alignment:
31032         switch (dataView.getUint16(tiffOffset)) {
31033         case 0x4949:
31034             littleEndian = true;
31035             break;
31036         case 0x4D4D:
31037             littleEndian = false;
31038             break;
31039         default:
31040             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31041             return;
31042         }
31043         // Check for the TIFF tag marker (0x002A):
31044         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31045             Roo.log('Invalid Exif data: Missing TIFF marker.');
31046             return;
31047         }
31048         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31049         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31050         
31051         this.parseExifTags(
31052             dataView,
31053             tiffOffset,
31054             tiffOffset + dirOffset,
31055             littleEndian
31056         );
31057     },
31058     
31059     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31060     {
31061         var tagsNumber,
31062             dirEndOffset,
31063             i;
31064         if (dirOffset + 6 > dataView.byteLength) {
31065             Roo.log('Invalid Exif data: Invalid directory offset.');
31066             return;
31067         }
31068         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31069         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31070         if (dirEndOffset + 4 > dataView.byteLength) {
31071             Roo.log('Invalid Exif data: Invalid directory size.');
31072             return;
31073         }
31074         for (i = 0; i < tagsNumber; i += 1) {
31075             this.parseExifTag(
31076                 dataView,
31077                 tiffOffset,
31078                 dirOffset + 2 + 12 * i, // tag offset
31079                 littleEndian
31080             );
31081         }
31082         // Return the offset to the next directory:
31083         return dataView.getUint32(dirEndOffset, littleEndian);
31084     },
31085     
31086     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31087     {
31088         var tag = dataView.getUint16(offset, littleEndian);
31089         
31090         this.exif[tag] = this.getExifValue(
31091             dataView,
31092             tiffOffset,
31093             offset,
31094             dataView.getUint16(offset + 2, littleEndian), // tag type
31095             dataView.getUint32(offset + 4, littleEndian), // tag length
31096             littleEndian
31097         );
31098     },
31099     
31100     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31101     {
31102         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31103             tagSize,
31104             dataOffset,
31105             values,
31106             i,
31107             str,
31108             c;
31109     
31110         if (!tagType) {
31111             Roo.log('Invalid Exif data: Invalid tag type.');
31112             return;
31113         }
31114         
31115         tagSize = tagType.size * length;
31116         // Determine if the value is contained in the dataOffset bytes,
31117         // or if the value at the dataOffset is a pointer to the actual data:
31118         dataOffset = tagSize > 4 ?
31119                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31120         if (dataOffset + tagSize > dataView.byteLength) {
31121             Roo.log('Invalid Exif data: Invalid data offset.');
31122             return;
31123         }
31124         if (length === 1) {
31125             return tagType.getValue(dataView, dataOffset, littleEndian);
31126         }
31127         values = [];
31128         for (i = 0; i < length; i += 1) {
31129             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31130         }
31131         
31132         if (tagType.ascii) {
31133             str = '';
31134             // Concatenate the chars:
31135             for (i = 0; i < values.length; i += 1) {
31136                 c = values[i];
31137                 // Ignore the terminating NULL byte(s):
31138                 if (c === '\u0000') {
31139                     break;
31140                 }
31141                 str += c;
31142             }
31143             return str;
31144         }
31145         return values;
31146     }
31147     
31148 });
31149
31150 Roo.apply(Roo.bootstrap.UploadCropbox, {
31151     tags : {
31152         'Orientation': 0x0112
31153     },
31154     
31155     Orientation: {
31156             1: 0, //'top-left',
31157 //            2: 'top-right',
31158             3: 180, //'bottom-right',
31159 //            4: 'bottom-left',
31160 //            5: 'left-top',
31161             6: 90, //'right-top',
31162 //            7: 'right-bottom',
31163             8: 270 //'left-bottom'
31164     },
31165     
31166     exifTagTypes : {
31167         // byte, 8-bit unsigned int:
31168         1: {
31169             getValue: function (dataView, dataOffset) {
31170                 return dataView.getUint8(dataOffset);
31171             },
31172             size: 1
31173         },
31174         // ascii, 8-bit byte:
31175         2: {
31176             getValue: function (dataView, dataOffset) {
31177                 return String.fromCharCode(dataView.getUint8(dataOffset));
31178             },
31179             size: 1,
31180             ascii: true
31181         },
31182         // short, 16 bit int:
31183         3: {
31184             getValue: function (dataView, dataOffset, littleEndian) {
31185                 return dataView.getUint16(dataOffset, littleEndian);
31186             },
31187             size: 2
31188         },
31189         // long, 32 bit int:
31190         4: {
31191             getValue: function (dataView, dataOffset, littleEndian) {
31192                 return dataView.getUint32(dataOffset, littleEndian);
31193             },
31194             size: 4
31195         },
31196         // rational = two long values, first is numerator, second is denominator:
31197         5: {
31198             getValue: function (dataView, dataOffset, littleEndian) {
31199                 return dataView.getUint32(dataOffset, littleEndian) /
31200                     dataView.getUint32(dataOffset + 4, littleEndian);
31201             },
31202             size: 8
31203         },
31204         // slong, 32 bit signed int:
31205         9: {
31206             getValue: function (dataView, dataOffset, littleEndian) {
31207                 return dataView.getInt32(dataOffset, littleEndian);
31208             },
31209             size: 4
31210         },
31211         // srational, two slongs, first is numerator, second is denominator:
31212         10: {
31213             getValue: function (dataView, dataOffset, littleEndian) {
31214                 return dataView.getInt32(dataOffset, littleEndian) /
31215                     dataView.getInt32(dataOffset + 4, littleEndian);
31216             },
31217             size: 8
31218         }
31219     },
31220     
31221     footer : {
31222         STANDARD : [
31223             {
31224                 tag : 'div',
31225                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31226                 action : 'rotate-left',
31227                 cn : [
31228                     {
31229                         tag : 'button',
31230                         cls : 'btn btn-default',
31231                         html : '<i class="fa fa-undo"></i>'
31232                     }
31233                 ]
31234             },
31235             {
31236                 tag : 'div',
31237                 cls : 'btn-group roo-upload-cropbox-picture',
31238                 action : 'picture',
31239                 cn : [
31240                     {
31241                         tag : 'button',
31242                         cls : 'btn btn-default',
31243                         html : '<i class="fa fa-picture-o"></i>'
31244                     }
31245                 ]
31246             },
31247             {
31248                 tag : 'div',
31249                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31250                 action : 'rotate-right',
31251                 cn : [
31252                     {
31253                         tag : 'button',
31254                         cls : 'btn btn-default',
31255                         html : '<i class="fa fa-repeat"></i>'
31256                     }
31257                 ]
31258             }
31259         ],
31260         DOCUMENT : [
31261             {
31262                 tag : 'div',
31263                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31264                 action : 'rotate-left',
31265                 cn : [
31266                     {
31267                         tag : 'button',
31268                         cls : 'btn btn-default',
31269                         html : '<i class="fa fa-undo"></i>'
31270                     }
31271                 ]
31272             },
31273             {
31274                 tag : 'div',
31275                 cls : 'btn-group roo-upload-cropbox-download',
31276                 action : 'download',
31277                 cn : [
31278                     {
31279                         tag : 'button',
31280                         cls : 'btn btn-default',
31281                         html : '<i class="fa fa-download"></i>'
31282                     }
31283                 ]
31284             },
31285             {
31286                 tag : 'div',
31287                 cls : 'btn-group roo-upload-cropbox-crop',
31288                 action : 'crop',
31289                 cn : [
31290                     {
31291                         tag : 'button',
31292                         cls : 'btn btn-default',
31293                         html : '<i class="fa fa-crop"></i>'
31294                     }
31295                 ]
31296             },
31297             {
31298                 tag : 'div',
31299                 cls : 'btn-group roo-upload-cropbox-trash',
31300                 action : 'trash',
31301                 cn : [
31302                     {
31303                         tag : 'button',
31304                         cls : 'btn btn-default',
31305                         html : '<i class="fa fa-trash"></i>'
31306                     }
31307                 ]
31308             },
31309             {
31310                 tag : 'div',
31311                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31312                 action : 'rotate-right',
31313                 cn : [
31314                     {
31315                         tag : 'button',
31316                         cls : 'btn btn-default',
31317                         html : '<i class="fa fa-repeat"></i>'
31318                     }
31319                 ]
31320             }
31321         ],
31322         ROTATOR : [
31323             {
31324                 tag : 'div',
31325                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31326                 action : 'rotate-left',
31327                 cn : [
31328                     {
31329                         tag : 'button',
31330                         cls : 'btn btn-default',
31331                         html : '<i class="fa fa-undo"></i>'
31332                     }
31333                 ]
31334             },
31335             {
31336                 tag : 'div',
31337                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31338                 action : 'rotate-right',
31339                 cn : [
31340                     {
31341                         tag : 'button',
31342                         cls : 'btn btn-default',
31343                         html : '<i class="fa fa-repeat"></i>'
31344                     }
31345                 ]
31346             }
31347         ]
31348     }
31349 });
31350
31351 /*
31352 * Licence: LGPL
31353 */
31354
31355 /**
31356  * @class Roo.bootstrap.DocumentManager
31357  * @extends Roo.bootstrap.Component
31358  * Bootstrap DocumentManager class
31359  * @cfg {String} paramName default 'imageUpload'
31360  * @cfg {String} toolTipName default 'filename'
31361  * @cfg {String} method default POST
31362  * @cfg {String} url action url
31363  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31364  * @cfg {Boolean} multiple multiple upload default true
31365  * @cfg {Number} thumbSize default 300
31366  * @cfg {String} fieldLabel
31367  * @cfg {Number} labelWidth default 4
31368  * @cfg {String} labelAlign (left|top) default left
31369  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31370 * @cfg {Number} labellg set the width of label (1-12)
31371  * @cfg {Number} labelmd set the width of label (1-12)
31372  * @cfg {Number} labelsm set the width of label (1-12)
31373  * @cfg {Number} labelxs set the width of label (1-12)
31374  * 
31375  * @constructor
31376  * Create a new DocumentManager
31377  * @param {Object} config The config object
31378  */
31379
31380 Roo.bootstrap.DocumentManager = function(config){
31381     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31382     
31383     this.files = [];
31384     this.delegates = [];
31385     
31386     this.addEvents({
31387         /**
31388          * @event initial
31389          * Fire when initial the DocumentManager
31390          * @param {Roo.bootstrap.DocumentManager} this
31391          */
31392         "initial" : true,
31393         /**
31394          * @event inspect
31395          * inspect selected file
31396          * @param {Roo.bootstrap.DocumentManager} this
31397          * @param {File} file
31398          */
31399         "inspect" : true,
31400         /**
31401          * @event exception
31402          * Fire when xhr load exception
31403          * @param {Roo.bootstrap.DocumentManager} this
31404          * @param {XMLHttpRequest} xhr
31405          */
31406         "exception" : true,
31407         /**
31408          * @event afterupload
31409          * Fire when xhr load exception
31410          * @param {Roo.bootstrap.DocumentManager} this
31411          * @param {XMLHttpRequest} xhr
31412          */
31413         "afterupload" : true,
31414         /**
31415          * @event prepare
31416          * prepare the form data
31417          * @param {Roo.bootstrap.DocumentManager} this
31418          * @param {Object} formData
31419          */
31420         "prepare" : true,
31421         /**
31422          * @event remove
31423          * Fire when remove the file
31424          * @param {Roo.bootstrap.DocumentManager} this
31425          * @param {Object} file
31426          */
31427         "remove" : true,
31428         /**
31429          * @event refresh
31430          * Fire after refresh the file
31431          * @param {Roo.bootstrap.DocumentManager} this
31432          */
31433         "refresh" : true,
31434         /**
31435          * @event click
31436          * Fire after click the image
31437          * @param {Roo.bootstrap.DocumentManager} this
31438          * @param {Object} file
31439          */
31440         "click" : true,
31441         /**
31442          * @event edit
31443          * Fire when upload a image and editable set to true
31444          * @param {Roo.bootstrap.DocumentManager} this
31445          * @param {Object} file
31446          */
31447         "edit" : true,
31448         /**
31449          * @event beforeselectfile
31450          * Fire before select file
31451          * @param {Roo.bootstrap.DocumentManager} this
31452          */
31453         "beforeselectfile" : true,
31454         /**
31455          * @event process
31456          * Fire before process file
31457          * @param {Roo.bootstrap.DocumentManager} this
31458          * @param {Object} file
31459          */
31460         "process" : true,
31461         /**
31462          * @event previewrendered
31463          * Fire when preview rendered
31464          * @param {Roo.bootstrap.DocumentManager} this
31465          * @param {Object} file
31466          */
31467         "previewrendered" : true,
31468         /**
31469          */
31470         "previewResize" : true
31471         
31472     });
31473 };
31474
31475 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31476     
31477     boxes : 0,
31478     inputName : '',
31479     thumbSize : 300,
31480     multiple : true,
31481     files : false,
31482     method : 'POST',
31483     url : '',
31484     paramName : 'imageUpload',
31485     toolTipName : 'filename',
31486     fieldLabel : '',
31487     labelWidth : 4,
31488     labelAlign : 'left',
31489     editable : true,
31490     delegates : false,
31491     xhr : false, 
31492     
31493     labellg : 0,
31494     labelmd : 0,
31495     labelsm : 0,
31496     labelxs : 0,
31497     
31498     getAutoCreate : function()
31499     {   
31500         var managerWidget = {
31501             tag : 'div',
31502             cls : 'roo-document-manager',
31503             cn : [
31504                 {
31505                     tag : 'input',
31506                     cls : 'roo-document-manager-selector',
31507                     type : 'file'
31508                 },
31509                 {
31510                     tag : 'div',
31511                     cls : 'roo-document-manager-uploader',
31512                     cn : [
31513                         {
31514                             tag : 'div',
31515                             cls : 'roo-document-manager-upload-btn',
31516                             html : '<i class="fa fa-plus"></i>'
31517                         }
31518                     ]
31519                     
31520                 }
31521             ]
31522         };
31523         
31524         var content = [
31525             {
31526                 tag : 'div',
31527                 cls : 'column col-md-12',
31528                 cn : managerWidget
31529             }
31530         ];
31531         
31532         if(this.fieldLabel.length){
31533             
31534             content = [
31535                 {
31536                     tag : 'div',
31537                     cls : 'column col-md-12',
31538                     html : this.fieldLabel
31539                 },
31540                 {
31541                     tag : 'div',
31542                     cls : 'column col-md-12',
31543                     cn : managerWidget
31544                 }
31545             ];
31546
31547             if(this.labelAlign == 'left'){
31548                 content = [
31549                     {
31550                         tag : 'div',
31551                         cls : 'column',
31552                         html : this.fieldLabel
31553                     },
31554                     {
31555                         tag : 'div',
31556                         cls : 'column',
31557                         cn : managerWidget
31558                     }
31559                 ];
31560                 
31561                 if(this.labelWidth > 12){
31562                     content[0].style = "width: " + this.labelWidth + 'px';
31563                 }
31564
31565                 if(this.labelWidth < 13 && this.labelmd == 0){
31566                     this.labelmd = this.labelWidth;
31567                 }
31568
31569                 if(this.labellg > 0){
31570                     content[0].cls += ' col-lg-' + this.labellg;
31571                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31572                 }
31573
31574                 if(this.labelmd > 0){
31575                     content[0].cls += ' col-md-' + this.labelmd;
31576                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31577                 }
31578
31579                 if(this.labelsm > 0){
31580                     content[0].cls += ' col-sm-' + this.labelsm;
31581                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31582                 }
31583
31584                 if(this.labelxs > 0){
31585                     content[0].cls += ' col-xs-' + this.labelxs;
31586                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31587                 }
31588                 
31589             }
31590         }
31591         
31592         var cfg = {
31593             tag : 'div',
31594             cls : 'row clearfix',
31595             cn : content
31596         };
31597         
31598         return cfg;
31599         
31600     },
31601     
31602     initEvents : function()
31603     {
31604         this.managerEl = this.el.select('.roo-document-manager', true).first();
31605         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31606         
31607         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31608         this.selectorEl.hide();
31609         
31610         if(this.multiple){
31611             this.selectorEl.attr('multiple', 'multiple');
31612         }
31613         
31614         this.selectorEl.on('change', this.onFileSelected, this);
31615         
31616         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31617         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31618         
31619         this.uploader.on('click', this.onUploaderClick, this);
31620         
31621         this.renderProgressDialog();
31622         
31623         var _this = this;
31624         
31625         window.addEventListener("resize", function() { _this.refresh(); } );
31626         
31627         this.fireEvent('initial', this);
31628     },
31629     
31630     renderProgressDialog : function()
31631     {
31632         var _this = this;
31633         
31634         this.progressDialog = new Roo.bootstrap.Modal({
31635             cls : 'roo-document-manager-progress-dialog',
31636             allow_close : false,
31637             animate : false,
31638             title : '',
31639             buttons : [
31640                 {
31641                     name  :'cancel',
31642                     weight : 'danger',
31643                     html : 'Cancel'
31644                 }
31645             ], 
31646             listeners : { 
31647                 btnclick : function() {
31648                     _this.uploadCancel();
31649                     this.hide();
31650                 }
31651             }
31652         });
31653          
31654         this.progressDialog.render(Roo.get(document.body));
31655          
31656         this.progress = new Roo.bootstrap.Progress({
31657             cls : 'roo-document-manager-progress',
31658             active : true,
31659             striped : true
31660         });
31661         
31662         this.progress.render(this.progressDialog.getChildContainer());
31663         
31664         this.progressBar = new Roo.bootstrap.ProgressBar({
31665             cls : 'roo-document-manager-progress-bar',
31666             aria_valuenow : 0,
31667             aria_valuemin : 0,
31668             aria_valuemax : 12,
31669             panel : 'success'
31670         });
31671         
31672         this.progressBar.render(this.progress.getChildContainer());
31673     },
31674     
31675     onUploaderClick : function(e)
31676     {
31677         e.preventDefault();
31678      
31679         if(this.fireEvent('beforeselectfile', this) != false){
31680             this.selectorEl.dom.click();
31681         }
31682         
31683     },
31684     
31685     onFileSelected : function(e)
31686     {
31687         e.preventDefault();
31688         
31689         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31690             return;
31691         }
31692         
31693         Roo.each(this.selectorEl.dom.files, function(file){
31694             if(this.fireEvent('inspect', this, file) != false){
31695                 this.files.push(file);
31696             }
31697         }, this);
31698         
31699         this.queue();
31700         
31701     },
31702     
31703     queue : function()
31704     {
31705         this.selectorEl.dom.value = '';
31706         
31707         if(!this.files || !this.files.length){
31708             return;
31709         }
31710         
31711         if(this.boxes > 0 && this.files.length > this.boxes){
31712             this.files = this.files.slice(0, this.boxes);
31713         }
31714         
31715         this.uploader.show();
31716         
31717         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31718             this.uploader.hide();
31719         }
31720         
31721         var _this = this;
31722         
31723         var files = [];
31724         
31725         var docs = [];
31726         
31727         Roo.each(this.files, function(file){
31728             
31729             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31730                 var f = this.renderPreview(file);
31731                 files.push(f);
31732                 return;
31733             }
31734             
31735             if(file.type.indexOf('image') != -1){
31736                 this.delegates.push(
31737                     (function(){
31738                         _this.process(file);
31739                     }).createDelegate(this)
31740                 );
31741         
31742                 return;
31743             }
31744             
31745             docs.push(
31746                 (function(){
31747                     _this.process(file);
31748                 }).createDelegate(this)
31749             );
31750             
31751         }, this);
31752         
31753         this.files = files;
31754         
31755         this.delegates = this.delegates.concat(docs);
31756         
31757         if(!this.delegates.length){
31758             this.refresh();
31759             return;
31760         }
31761         
31762         this.progressBar.aria_valuemax = this.delegates.length;
31763         
31764         this.arrange();
31765         
31766         return;
31767     },
31768     
31769     arrange : function()
31770     {
31771         if(!this.delegates.length){
31772             this.progressDialog.hide();
31773             this.refresh();
31774             return;
31775         }
31776         
31777         var delegate = this.delegates.shift();
31778         
31779         this.progressDialog.show();
31780         
31781         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31782         
31783         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31784         
31785         delegate();
31786     },
31787     
31788     refresh : function()
31789     {
31790         this.uploader.show();
31791         
31792         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31793             this.uploader.hide();
31794         }
31795         
31796         Roo.isTouch ? this.closable(false) : this.closable(true);
31797         
31798         this.fireEvent('refresh', this);
31799     },
31800     
31801     onRemove : function(e, el, o)
31802     {
31803         e.preventDefault();
31804         
31805         this.fireEvent('remove', this, o);
31806         
31807     },
31808     
31809     remove : function(o)
31810     {
31811         var files = [];
31812         
31813         Roo.each(this.files, function(file){
31814             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31815                 files.push(file);
31816                 return;
31817             }
31818
31819             o.target.remove();
31820
31821         }, this);
31822         
31823         this.files = files;
31824         
31825         this.refresh();
31826     },
31827     
31828     clear : function()
31829     {
31830         Roo.each(this.files, function(file){
31831             if(!file.target){
31832                 return;
31833             }
31834             
31835             file.target.remove();
31836
31837         }, this);
31838         
31839         this.files = [];
31840         
31841         this.refresh();
31842     },
31843     
31844     onClick : function(e, el, o)
31845     {
31846         e.preventDefault();
31847         
31848         this.fireEvent('click', this, o);
31849         
31850     },
31851     
31852     closable : function(closable)
31853     {
31854         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31855             
31856             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31857             
31858             if(closable){
31859                 el.show();
31860                 return;
31861             }
31862             
31863             el.hide();
31864             
31865         }, this);
31866     },
31867     
31868     xhrOnLoad : function(xhr)
31869     {
31870         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31871             el.remove();
31872         }, this);
31873         
31874         if (xhr.readyState !== 4) {
31875             this.arrange();
31876             this.fireEvent('exception', this, xhr);
31877             return;
31878         }
31879
31880         var response = Roo.decode(xhr.responseText);
31881         
31882         if(!response.success){
31883             this.arrange();
31884             this.fireEvent('exception', this, xhr);
31885             return;
31886         }
31887         
31888         var file = this.renderPreview(response.data);
31889         
31890         this.files.push(file);
31891         
31892         this.arrange();
31893         
31894         this.fireEvent('afterupload', this, xhr);
31895         
31896     },
31897     
31898     xhrOnError : function(xhr)
31899     {
31900         Roo.log('xhr on error');
31901         
31902         var response = Roo.decode(xhr.responseText);
31903           
31904         Roo.log(response);
31905         
31906         this.arrange();
31907     },
31908     
31909     process : function(file)
31910     {
31911         if(this.fireEvent('process', this, file) !== false){
31912             if(this.editable && file.type.indexOf('image') != -1){
31913                 this.fireEvent('edit', this, file);
31914                 return;
31915             }
31916
31917             this.uploadStart(file, false);
31918
31919             return;
31920         }
31921         
31922     },
31923     
31924     uploadStart : function(file, crop)
31925     {
31926         this.xhr = new XMLHttpRequest();
31927         
31928         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31929             this.arrange();
31930             return;
31931         }
31932         
31933         file.xhr = this.xhr;
31934             
31935         this.managerEl.createChild({
31936             tag : 'div',
31937             cls : 'roo-document-manager-loading',
31938             cn : [
31939                 {
31940                     tag : 'div',
31941                     tooltip : file.name,
31942                     cls : 'roo-document-manager-thumb',
31943                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31944                 }
31945             ]
31946
31947         });
31948
31949         this.xhr.open(this.method, this.url, true);
31950         
31951         var headers = {
31952             "Accept": "application/json",
31953             "Cache-Control": "no-cache",
31954             "X-Requested-With": "XMLHttpRequest"
31955         };
31956         
31957         for (var headerName in headers) {
31958             var headerValue = headers[headerName];
31959             if (headerValue) {
31960                 this.xhr.setRequestHeader(headerName, headerValue);
31961             }
31962         }
31963         
31964         var _this = this;
31965         
31966         this.xhr.onload = function()
31967         {
31968             _this.xhrOnLoad(_this.xhr);
31969         }
31970         
31971         this.xhr.onerror = function()
31972         {
31973             _this.xhrOnError(_this.xhr);
31974         }
31975         
31976         var formData = new FormData();
31977
31978         formData.append('returnHTML', 'NO');
31979         
31980         if(crop){
31981             formData.append('crop', crop);
31982         }
31983         
31984         formData.append(this.paramName, file, file.name);
31985         
31986         var options = {
31987             file : file, 
31988             manually : false
31989         };
31990         
31991         if(this.fireEvent('prepare', this, formData, options) != false){
31992             
31993             if(options.manually){
31994                 return;
31995             }
31996             
31997             this.xhr.send(formData);
31998             return;
31999         };
32000         
32001         this.uploadCancel();
32002     },
32003     
32004     uploadCancel : function()
32005     {
32006         if (this.xhr) {
32007             this.xhr.abort();
32008         }
32009         
32010         this.delegates = [];
32011         
32012         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32013             el.remove();
32014         }, this);
32015         
32016         this.arrange();
32017     },
32018     
32019     renderPreview : function(file)
32020     {
32021         if(typeof(file.target) != 'undefined' && file.target){
32022             return file;
32023         }
32024         
32025         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32026         
32027         var previewEl = this.managerEl.createChild({
32028             tag : 'div',
32029             cls : 'roo-document-manager-preview',
32030             cn : [
32031                 {
32032                     tag : 'div',
32033                     tooltip : file[this.toolTipName],
32034                     cls : 'roo-document-manager-thumb',
32035                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32036                 },
32037                 {
32038                     tag : 'button',
32039                     cls : 'close',
32040                     html : '<i class="fa fa-times-circle"></i>'
32041                 }
32042             ]
32043         });
32044
32045         var close = previewEl.select('button.close', true).first();
32046
32047         close.on('click', this.onRemove, this, file);
32048
32049         file.target = previewEl;
32050
32051         var image = previewEl.select('img', true).first();
32052         
32053         var _this = this;
32054         
32055         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32056         
32057         image.on('click', this.onClick, this, file);
32058         
32059         this.fireEvent('previewrendered', this, file);
32060         
32061         return file;
32062         
32063     },
32064     
32065     onPreviewLoad : function(file, image)
32066     {
32067         if(typeof(file.target) == 'undefined' || !file.target){
32068             return;
32069         }
32070         
32071         var width = image.dom.naturalWidth || image.dom.width;
32072         var height = image.dom.naturalHeight || image.dom.height;
32073         
32074         if(!this.previewResize) {
32075             return;
32076         }
32077         
32078         if(width > height){
32079             file.target.addClass('wide');
32080             return;
32081         }
32082         
32083         file.target.addClass('tall');
32084         return;
32085         
32086     },
32087     
32088     uploadFromSource : function(file, crop)
32089     {
32090         this.xhr = new XMLHttpRequest();
32091         
32092         this.managerEl.createChild({
32093             tag : 'div',
32094             cls : 'roo-document-manager-loading',
32095             cn : [
32096                 {
32097                     tag : 'div',
32098                     tooltip : file.name,
32099                     cls : 'roo-document-manager-thumb',
32100                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32101                 }
32102             ]
32103
32104         });
32105
32106         this.xhr.open(this.method, this.url, true);
32107         
32108         var headers = {
32109             "Accept": "application/json",
32110             "Cache-Control": "no-cache",
32111             "X-Requested-With": "XMLHttpRequest"
32112         };
32113         
32114         for (var headerName in headers) {
32115             var headerValue = headers[headerName];
32116             if (headerValue) {
32117                 this.xhr.setRequestHeader(headerName, headerValue);
32118             }
32119         }
32120         
32121         var _this = this;
32122         
32123         this.xhr.onload = function()
32124         {
32125             _this.xhrOnLoad(_this.xhr);
32126         }
32127         
32128         this.xhr.onerror = function()
32129         {
32130             _this.xhrOnError(_this.xhr);
32131         }
32132         
32133         var formData = new FormData();
32134
32135         formData.append('returnHTML', 'NO');
32136         
32137         formData.append('crop', crop);
32138         
32139         if(typeof(file.filename) != 'undefined'){
32140             formData.append('filename', file.filename);
32141         }
32142         
32143         if(typeof(file.mimetype) != 'undefined'){
32144             formData.append('mimetype', file.mimetype);
32145         }
32146         
32147         Roo.log(formData);
32148         
32149         if(this.fireEvent('prepare', this, formData) != false){
32150             this.xhr.send(formData);
32151         };
32152     }
32153 });
32154
32155 /*
32156 * Licence: LGPL
32157 */
32158
32159 /**
32160  * @class Roo.bootstrap.DocumentViewer
32161  * @extends Roo.bootstrap.Component
32162  * Bootstrap DocumentViewer class
32163  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32164  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32165  * 
32166  * @constructor
32167  * Create a new DocumentViewer
32168  * @param {Object} config The config object
32169  */
32170
32171 Roo.bootstrap.DocumentViewer = function(config){
32172     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32173     
32174     this.addEvents({
32175         /**
32176          * @event initial
32177          * Fire after initEvent
32178          * @param {Roo.bootstrap.DocumentViewer} this
32179          */
32180         "initial" : true,
32181         /**
32182          * @event click
32183          * Fire after click
32184          * @param {Roo.bootstrap.DocumentViewer} this
32185          */
32186         "click" : true,
32187         /**
32188          * @event download
32189          * Fire after download button
32190          * @param {Roo.bootstrap.DocumentViewer} this
32191          */
32192         "download" : true,
32193         /**
32194          * @event trash
32195          * Fire after trash button
32196          * @param {Roo.bootstrap.DocumentViewer} this
32197          */
32198         "trash" : true
32199         
32200     });
32201 };
32202
32203 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32204     
32205     showDownload : true,
32206     
32207     showTrash : true,
32208     
32209     getAutoCreate : function()
32210     {
32211         var cfg = {
32212             tag : 'div',
32213             cls : 'roo-document-viewer',
32214             cn : [
32215                 {
32216                     tag : 'div',
32217                     cls : 'roo-document-viewer-body',
32218                     cn : [
32219                         {
32220                             tag : 'div',
32221                             cls : 'roo-document-viewer-thumb',
32222                             cn : [
32223                                 {
32224                                     tag : 'img',
32225                                     cls : 'roo-document-viewer-image'
32226                                 }
32227                             ]
32228                         }
32229                     ]
32230                 },
32231                 {
32232                     tag : 'div',
32233                     cls : 'roo-document-viewer-footer',
32234                     cn : {
32235                         tag : 'div',
32236                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32237                         cn : [
32238                             {
32239                                 tag : 'div',
32240                                 cls : 'btn-group roo-document-viewer-download',
32241                                 cn : [
32242                                     {
32243                                         tag : 'button',
32244                                         cls : 'btn btn-default',
32245                                         html : '<i class="fa fa-download"></i>'
32246                                     }
32247                                 ]
32248                             },
32249                             {
32250                                 tag : 'div',
32251                                 cls : 'btn-group roo-document-viewer-trash',
32252                                 cn : [
32253                                     {
32254                                         tag : 'button',
32255                                         cls : 'btn btn-default',
32256                                         html : '<i class="fa fa-trash"></i>'
32257                                     }
32258                                 ]
32259                             }
32260                         ]
32261                     }
32262                 }
32263             ]
32264         };
32265         
32266         return cfg;
32267     },
32268     
32269     initEvents : function()
32270     {
32271         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32272         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32273         
32274         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32275         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32276         
32277         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32278         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32279         
32280         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32281         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32282         
32283         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32284         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32285         
32286         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32287         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32288         
32289         this.bodyEl.on('click', this.onClick, this);
32290         this.downloadBtn.on('click', this.onDownload, this);
32291         this.trashBtn.on('click', this.onTrash, this);
32292         
32293         this.downloadBtn.hide();
32294         this.trashBtn.hide();
32295         
32296         if(this.showDownload){
32297             this.downloadBtn.show();
32298         }
32299         
32300         if(this.showTrash){
32301             this.trashBtn.show();
32302         }
32303         
32304         if(!this.showDownload && !this.showTrash) {
32305             this.footerEl.hide();
32306         }
32307         
32308     },
32309     
32310     initial : function()
32311     {
32312         this.fireEvent('initial', this);
32313         
32314     },
32315     
32316     onClick : function(e)
32317     {
32318         e.preventDefault();
32319         
32320         this.fireEvent('click', this);
32321     },
32322     
32323     onDownload : function(e)
32324     {
32325         e.preventDefault();
32326         
32327         this.fireEvent('download', this);
32328     },
32329     
32330     onTrash : function(e)
32331     {
32332         e.preventDefault();
32333         
32334         this.fireEvent('trash', this);
32335     }
32336     
32337 });
32338 /*
32339  * - LGPL
32340  *
32341  * nav progress bar
32342  * 
32343  */
32344
32345 /**
32346  * @class Roo.bootstrap.NavProgressBar
32347  * @extends Roo.bootstrap.Component
32348  * Bootstrap NavProgressBar class
32349  * 
32350  * @constructor
32351  * Create a new nav progress bar
32352  * @param {Object} config The config object
32353  */
32354
32355 Roo.bootstrap.NavProgressBar = function(config){
32356     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32357
32358     this.bullets = this.bullets || [];
32359    
32360 //    Roo.bootstrap.NavProgressBar.register(this);
32361      this.addEvents({
32362         /**
32363              * @event changed
32364              * Fires when the active item changes
32365              * @param {Roo.bootstrap.NavProgressBar} this
32366              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32367              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32368          */
32369         'changed': true
32370      });
32371     
32372 };
32373
32374 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32375     
32376     bullets : [],
32377     barItems : [],
32378     
32379     getAutoCreate : function()
32380     {
32381         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32382         
32383         cfg = {
32384             tag : 'div',
32385             cls : 'roo-navigation-bar-group',
32386             cn : [
32387                 {
32388                     tag : 'div',
32389                     cls : 'roo-navigation-top-bar'
32390                 },
32391                 {
32392                     tag : 'div',
32393                     cls : 'roo-navigation-bullets-bar',
32394                     cn : [
32395                         {
32396                             tag : 'ul',
32397                             cls : 'roo-navigation-bar'
32398                         }
32399                     ]
32400                 },
32401                 
32402                 {
32403                     tag : 'div',
32404                     cls : 'roo-navigation-bottom-bar'
32405                 }
32406             ]
32407             
32408         };
32409         
32410         return cfg;
32411         
32412     },
32413     
32414     initEvents: function() 
32415     {
32416         
32417     },
32418     
32419     onRender : function(ct, position) 
32420     {
32421         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32422         
32423         if(this.bullets.length){
32424             Roo.each(this.bullets, function(b){
32425                this.addItem(b);
32426             }, this);
32427         }
32428         
32429         this.format();
32430         
32431     },
32432     
32433     addItem : function(cfg)
32434     {
32435         var item = new Roo.bootstrap.NavProgressItem(cfg);
32436         
32437         item.parentId = this.id;
32438         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32439         
32440         if(cfg.html){
32441             var top = new Roo.bootstrap.Element({
32442                 tag : 'div',
32443                 cls : 'roo-navigation-bar-text'
32444             });
32445             
32446             var bottom = new Roo.bootstrap.Element({
32447                 tag : 'div',
32448                 cls : 'roo-navigation-bar-text'
32449             });
32450             
32451             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32452             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32453             
32454             var topText = new Roo.bootstrap.Element({
32455                 tag : 'span',
32456                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32457             });
32458             
32459             var bottomText = new Roo.bootstrap.Element({
32460                 tag : 'span',
32461                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32462             });
32463             
32464             topText.onRender(top.el, null);
32465             bottomText.onRender(bottom.el, null);
32466             
32467             item.topEl = top;
32468             item.bottomEl = bottom;
32469         }
32470         
32471         this.barItems.push(item);
32472         
32473         return item;
32474     },
32475     
32476     getActive : function()
32477     {
32478         var active = false;
32479         
32480         Roo.each(this.barItems, function(v){
32481             
32482             if (!v.isActive()) {
32483                 return;
32484             }
32485             
32486             active = v;
32487             return false;
32488             
32489         });
32490         
32491         return active;
32492     },
32493     
32494     setActiveItem : function(item)
32495     {
32496         var prev = false;
32497         
32498         Roo.each(this.barItems, function(v){
32499             if (v.rid == item.rid) {
32500                 return ;
32501             }
32502             
32503             if (v.isActive()) {
32504                 v.setActive(false);
32505                 prev = v;
32506             }
32507         });
32508
32509         item.setActive(true);
32510         
32511         this.fireEvent('changed', this, item, prev);
32512     },
32513     
32514     getBarItem: function(rid)
32515     {
32516         var ret = false;
32517         
32518         Roo.each(this.barItems, function(e) {
32519             if (e.rid != rid) {
32520                 return;
32521             }
32522             
32523             ret =  e;
32524             return false;
32525         });
32526         
32527         return ret;
32528     },
32529     
32530     indexOfItem : function(item)
32531     {
32532         var index = false;
32533         
32534         Roo.each(this.barItems, function(v, i){
32535             
32536             if (v.rid != item.rid) {
32537                 return;
32538             }
32539             
32540             index = i;
32541             return false
32542         });
32543         
32544         return index;
32545     },
32546     
32547     setActiveNext : function()
32548     {
32549         var i = this.indexOfItem(this.getActive());
32550         
32551         if (i > this.barItems.length) {
32552             return;
32553         }
32554         
32555         this.setActiveItem(this.barItems[i+1]);
32556     },
32557     
32558     setActivePrev : function()
32559     {
32560         var i = this.indexOfItem(this.getActive());
32561         
32562         if (i  < 1) {
32563             return;
32564         }
32565         
32566         this.setActiveItem(this.barItems[i-1]);
32567     },
32568     
32569     format : function()
32570     {
32571         if(!this.barItems.length){
32572             return;
32573         }
32574      
32575         var width = 100 / this.barItems.length;
32576         
32577         Roo.each(this.barItems, function(i){
32578             i.el.setStyle('width', width + '%');
32579             i.topEl.el.setStyle('width', width + '%');
32580             i.bottomEl.el.setStyle('width', width + '%');
32581         }, this);
32582         
32583     }
32584     
32585 });
32586 /*
32587  * - LGPL
32588  *
32589  * Nav Progress Item
32590  * 
32591  */
32592
32593 /**
32594  * @class Roo.bootstrap.NavProgressItem
32595  * @extends Roo.bootstrap.Component
32596  * Bootstrap NavProgressItem class
32597  * @cfg {String} rid the reference id
32598  * @cfg {Boolean} active (true|false) Is item active default false
32599  * @cfg {Boolean} disabled (true|false) Is item active default false
32600  * @cfg {String} html
32601  * @cfg {String} position (top|bottom) text position default bottom
32602  * @cfg {String} icon show icon instead of number
32603  * 
32604  * @constructor
32605  * Create a new NavProgressItem
32606  * @param {Object} config The config object
32607  */
32608 Roo.bootstrap.NavProgressItem = function(config){
32609     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32610     this.addEvents({
32611         // raw events
32612         /**
32613          * @event click
32614          * The raw click event for the entire grid.
32615          * @param {Roo.bootstrap.NavProgressItem} this
32616          * @param {Roo.EventObject} e
32617          */
32618         "click" : true
32619     });
32620    
32621 };
32622
32623 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32624     
32625     rid : '',
32626     active : false,
32627     disabled : false,
32628     html : '',
32629     position : 'bottom',
32630     icon : false,
32631     
32632     getAutoCreate : function()
32633     {
32634         var iconCls = 'roo-navigation-bar-item-icon';
32635         
32636         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32637         
32638         var cfg = {
32639             tag: 'li',
32640             cls: 'roo-navigation-bar-item',
32641             cn : [
32642                 {
32643                     tag : 'i',
32644                     cls : iconCls
32645                 }
32646             ]
32647         };
32648         
32649         if(this.active){
32650             cfg.cls += ' active';
32651         }
32652         if(this.disabled){
32653             cfg.cls += ' disabled';
32654         }
32655         
32656         return cfg;
32657     },
32658     
32659     disable : function()
32660     {
32661         this.setDisabled(true);
32662     },
32663     
32664     enable : function()
32665     {
32666         this.setDisabled(false);
32667     },
32668     
32669     initEvents: function() 
32670     {
32671         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32672         
32673         this.iconEl.on('click', this.onClick, this);
32674     },
32675     
32676     onClick : function(e)
32677     {
32678         e.preventDefault();
32679         
32680         if(this.disabled){
32681             return;
32682         }
32683         
32684         if(this.fireEvent('click', this, e) === false){
32685             return;
32686         };
32687         
32688         this.parent().setActiveItem(this);
32689     },
32690     
32691     isActive: function () 
32692     {
32693         return this.active;
32694     },
32695     
32696     setActive : function(state)
32697     {
32698         if(this.active == state){
32699             return;
32700         }
32701         
32702         this.active = state;
32703         
32704         if (state) {
32705             this.el.addClass('active');
32706             return;
32707         }
32708         
32709         this.el.removeClass('active');
32710         
32711         return;
32712     },
32713     
32714     setDisabled : function(state)
32715     {
32716         if(this.disabled == state){
32717             return;
32718         }
32719         
32720         this.disabled = state;
32721         
32722         if (state) {
32723             this.el.addClass('disabled');
32724             return;
32725         }
32726         
32727         this.el.removeClass('disabled');
32728     },
32729     
32730     tooltipEl : function()
32731     {
32732         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32733     }
32734 });
32735  
32736
32737  /*
32738  * - LGPL
32739  *
32740  * FieldLabel
32741  * 
32742  */
32743
32744 /**
32745  * @class Roo.bootstrap.FieldLabel
32746  * @extends Roo.bootstrap.Component
32747  * Bootstrap FieldLabel class
32748  * @cfg {String} html contents of the element
32749  * @cfg {String} tag tag of the element default label
32750  * @cfg {String} cls class of the element
32751  * @cfg {String} target label target 
32752  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32753  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32754  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32755  * @cfg {String} iconTooltip default "This field is required"
32756  * @cfg {String} indicatorpos (left|right) default left
32757  * 
32758  * @constructor
32759  * Create a new FieldLabel
32760  * @param {Object} config The config object
32761  */
32762
32763 Roo.bootstrap.FieldLabel = function(config){
32764     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32765     
32766     this.addEvents({
32767             /**
32768              * @event invalid
32769              * Fires after the field has been marked as invalid.
32770              * @param {Roo.form.FieldLabel} this
32771              * @param {String} msg The validation message
32772              */
32773             invalid : true,
32774             /**
32775              * @event valid
32776              * Fires after the field has been validated with no errors.
32777              * @param {Roo.form.FieldLabel} this
32778              */
32779             valid : true
32780         });
32781 };
32782
32783 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32784     
32785     tag: 'label',
32786     cls: '',
32787     html: '',
32788     target: '',
32789     allowBlank : true,
32790     invalidClass : 'has-warning',
32791     validClass : 'has-success',
32792     iconTooltip : 'This field is required',
32793     indicatorpos : 'left',
32794     
32795     getAutoCreate : function(){
32796         
32797         var cls = "";
32798         if (!this.allowBlank) {
32799             cls  = "visible";
32800         }
32801         
32802         var cfg = {
32803             tag : this.tag,
32804             cls : 'roo-bootstrap-field-label ' + this.cls,
32805             for : this.target,
32806             cn : [
32807                 {
32808                     tag : 'i',
32809                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32810                     tooltip : this.iconTooltip
32811                 },
32812                 {
32813                     tag : 'span',
32814                     html : this.html
32815                 }
32816             ] 
32817         };
32818         
32819         if(this.indicatorpos == 'right'){
32820             var cfg = {
32821                 tag : this.tag,
32822                 cls : 'roo-bootstrap-field-label ' + this.cls,
32823                 for : this.target,
32824                 cn : [
32825                     {
32826                         tag : 'span',
32827                         html : this.html
32828                     },
32829                     {
32830                         tag : 'i',
32831                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32832                         tooltip : this.iconTooltip
32833                     }
32834                 ] 
32835             };
32836         }
32837         
32838         return cfg;
32839     },
32840     
32841     initEvents: function() 
32842     {
32843         Roo.bootstrap.Element.superclass.initEvents.call(this);
32844         
32845         this.indicator = this.indicatorEl();
32846         
32847         if(this.indicator){
32848             this.indicator.removeClass('visible');
32849             this.indicator.addClass('invisible');
32850         }
32851         
32852         Roo.bootstrap.FieldLabel.register(this);
32853     },
32854     
32855     indicatorEl : function()
32856     {
32857         var indicator = this.el.select('i.roo-required-indicator',true).first();
32858         
32859         if(!indicator){
32860             return false;
32861         }
32862         
32863         return indicator;
32864         
32865     },
32866     
32867     /**
32868      * Mark this field as valid
32869      */
32870     markValid : function()
32871     {
32872         if(this.indicator){
32873             this.indicator.removeClass('visible');
32874             this.indicator.addClass('invisible');
32875         }
32876         if (Roo.bootstrap.version == 3) {
32877             this.el.removeClass(this.invalidClass);
32878             this.el.addClass(this.validClass);
32879         } else {
32880             this.el.removeClass('is-invalid');
32881             this.el.addClass('is-valid');
32882         }
32883         
32884         
32885         this.fireEvent('valid', this);
32886     },
32887     
32888     /**
32889      * Mark this field as invalid
32890      * @param {String} msg The validation message
32891      */
32892     markInvalid : function(msg)
32893     {
32894         if(this.indicator){
32895             this.indicator.removeClass('invisible');
32896             this.indicator.addClass('visible');
32897         }
32898           if (Roo.bootstrap.version == 3) {
32899             this.el.removeClass(this.validClass);
32900             this.el.addClass(this.invalidClass);
32901         } else {
32902             this.el.removeClass('is-valid');
32903             this.el.addClass('is-invalid');
32904         }
32905         
32906         
32907         this.fireEvent('invalid', this, msg);
32908     }
32909     
32910    
32911 });
32912
32913 Roo.apply(Roo.bootstrap.FieldLabel, {
32914     
32915     groups: {},
32916     
32917      /**
32918     * register a FieldLabel Group
32919     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32920     */
32921     register : function(label)
32922     {
32923         if(this.groups.hasOwnProperty(label.target)){
32924             return;
32925         }
32926      
32927         this.groups[label.target] = label;
32928         
32929     },
32930     /**
32931     * fetch a FieldLabel Group based on the target
32932     * @param {string} target
32933     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32934     */
32935     get: function(target) {
32936         if (typeof(this.groups[target]) == 'undefined') {
32937             return false;
32938         }
32939         
32940         return this.groups[target] ;
32941     }
32942 });
32943
32944  
32945
32946  /*
32947  * - LGPL
32948  *
32949  * page DateSplitField.
32950  * 
32951  */
32952
32953
32954 /**
32955  * @class Roo.bootstrap.DateSplitField
32956  * @extends Roo.bootstrap.Component
32957  * Bootstrap DateSplitField class
32958  * @cfg {string} fieldLabel - the label associated
32959  * @cfg {Number} labelWidth set the width of label (0-12)
32960  * @cfg {String} labelAlign (top|left)
32961  * @cfg {Boolean} dayAllowBlank (true|false) default false
32962  * @cfg {Boolean} monthAllowBlank (true|false) default false
32963  * @cfg {Boolean} yearAllowBlank (true|false) default false
32964  * @cfg {string} dayPlaceholder 
32965  * @cfg {string} monthPlaceholder
32966  * @cfg {string} yearPlaceholder
32967  * @cfg {string} dayFormat default 'd'
32968  * @cfg {string} monthFormat default 'm'
32969  * @cfg {string} yearFormat default 'Y'
32970  * @cfg {Number} labellg set the width of label (1-12)
32971  * @cfg {Number} labelmd set the width of label (1-12)
32972  * @cfg {Number} labelsm set the width of label (1-12)
32973  * @cfg {Number} labelxs set the width of label (1-12)
32974
32975  *     
32976  * @constructor
32977  * Create a new DateSplitField
32978  * @param {Object} config The config object
32979  */
32980
32981 Roo.bootstrap.DateSplitField = function(config){
32982     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32983     
32984     this.addEvents({
32985         // raw events
32986          /**
32987          * @event years
32988          * getting the data of years
32989          * @param {Roo.bootstrap.DateSplitField} this
32990          * @param {Object} years
32991          */
32992         "years" : true,
32993         /**
32994          * @event days
32995          * getting the data of days
32996          * @param {Roo.bootstrap.DateSplitField} this
32997          * @param {Object} days
32998          */
32999         "days" : true,
33000         /**
33001          * @event invalid
33002          * Fires after the field has been marked as invalid.
33003          * @param {Roo.form.Field} this
33004          * @param {String} msg The validation message
33005          */
33006         invalid : true,
33007        /**
33008          * @event valid
33009          * Fires after the field has been validated with no errors.
33010          * @param {Roo.form.Field} this
33011          */
33012         valid : true
33013     });
33014 };
33015
33016 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33017     
33018     fieldLabel : '',
33019     labelAlign : 'top',
33020     labelWidth : 3,
33021     dayAllowBlank : false,
33022     monthAllowBlank : false,
33023     yearAllowBlank : false,
33024     dayPlaceholder : '',
33025     monthPlaceholder : '',
33026     yearPlaceholder : '',
33027     dayFormat : 'd',
33028     monthFormat : 'm',
33029     yearFormat : 'Y',
33030     isFormField : true,
33031     labellg : 0,
33032     labelmd : 0,
33033     labelsm : 0,
33034     labelxs : 0,
33035     
33036     getAutoCreate : function()
33037     {
33038         var cfg = {
33039             tag : 'div',
33040             cls : 'row roo-date-split-field-group',
33041             cn : [
33042                 {
33043                     tag : 'input',
33044                     type : 'hidden',
33045                     cls : 'form-hidden-field roo-date-split-field-group-value',
33046                     name : this.name
33047                 }
33048             ]
33049         };
33050         
33051         var labelCls = 'col-md-12';
33052         var contentCls = 'col-md-4';
33053         
33054         if(this.fieldLabel){
33055             
33056             var label = {
33057                 tag : 'div',
33058                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33059                 cn : [
33060                     {
33061                         tag : 'label',
33062                         html : this.fieldLabel
33063                     }
33064                 ]
33065             };
33066             
33067             if(this.labelAlign == 'left'){
33068             
33069                 if(this.labelWidth > 12){
33070                     label.style = "width: " + this.labelWidth + 'px';
33071                 }
33072
33073                 if(this.labelWidth < 13 && this.labelmd == 0){
33074                     this.labelmd = this.labelWidth;
33075                 }
33076
33077                 if(this.labellg > 0){
33078                     labelCls = ' col-lg-' + this.labellg;
33079                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33080                 }
33081
33082                 if(this.labelmd > 0){
33083                     labelCls = ' col-md-' + this.labelmd;
33084                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33085                 }
33086
33087                 if(this.labelsm > 0){
33088                     labelCls = ' col-sm-' + this.labelsm;
33089                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33090                 }
33091
33092                 if(this.labelxs > 0){
33093                     labelCls = ' col-xs-' + this.labelxs;
33094                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33095                 }
33096             }
33097             
33098             label.cls += ' ' + labelCls;
33099             
33100             cfg.cn.push(label);
33101         }
33102         
33103         Roo.each(['day', 'month', 'year'], function(t){
33104             cfg.cn.push({
33105                 tag : 'div',
33106                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33107             });
33108         }, this);
33109         
33110         return cfg;
33111     },
33112     
33113     inputEl: function ()
33114     {
33115         return this.el.select('.roo-date-split-field-group-value', true).first();
33116     },
33117     
33118     onRender : function(ct, position) 
33119     {
33120         var _this = this;
33121         
33122         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33123         
33124         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33125         
33126         this.dayField = new Roo.bootstrap.ComboBox({
33127             allowBlank : this.dayAllowBlank,
33128             alwaysQuery : true,
33129             displayField : 'value',
33130             editable : false,
33131             fieldLabel : '',
33132             forceSelection : true,
33133             mode : 'local',
33134             placeholder : this.dayPlaceholder,
33135             selectOnFocus : true,
33136             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33137             triggerAction : 'all',
33138             typeAhead : true,
33139             valueField : 'value',
33140             store : new Roo.data.SimpleStore({
33141                 data : (function() {    
33142                     var days = [];
33143                     _this.fireEvent('days', _this, days);
33144                     return days;
33145                 })(),
33146                 fields : [ 'value' ]
33147             }),
33148             listeners : {
33149                 select : function (_self, record, index)
33150                 {
33151                     _this.setValue(_this.getValue());
33152                 }
33153             }
33154         });
33155
33156         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33157         
33158         this.monthField = new Roo.bootstrap.MonthField({
33159             after : '<i class=\"fa fa-calendar\"></i>',
33160             allowBlank : this.monthAllowBlank,
33161             placeholder : this.monthPlaceholder,
33162             readOnly : true,
33163             listeners : {
33164                 render : function (_self)
33165                 {
33166                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33167                         e.preventDefault();
33168                         _self.focus();
33169                     });
33170                 },
33171                 select : function (_self, oldvalue, newvalue)
33172                 {
33173                     _this.setValue(_this.getValue());
33174                 }
33175             }
33176         });
33177         
33178         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33179         
33180         this.yearField = new Roo.bootstrap.ComboBox({
33181             allowBlank : this.yearAllowBlank,
33182             alwaysQuery : true,
33183             displayField : 'value',
33184             editable : false,
33185             fieldLabel : '',
33186             forceSelection : true,
33187             mode : 'local',
33188             placeholder : this.yearPlaceholder,
33189             selectOnFocus : true,
33190             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33191             triggerAction : 'all',
33192             typeAhead : true,
33193             valueField : 'value',
33194             store : new Roo.data.SimpleStore({
33195                 data : (function() {
33196                     var years = [];
33197                     _this.fireEvent('years', _this, years);
33198                     return years;
33199                 })(),
33200                 fields : [ 'value' ]
33201             }),
33202             listeners : {
33203                 select : function (_self, record, index)
33204                 {
33205                     _this.setValue(_this.getValue());
33206                 }
33207             }
33208         });
33209
33210         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33211     },
33212     
33213     setValue : function(v, format)
33214     {
33215         this.inputEl.dom.value = v;
33216         
33217         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33218         
33219         var d = Date.parseDate(v, f);
33220         
33221         if(!d){
33222             this.validate();
33223             return;
33224         }
33225         
33226         this.setDay(d.format(this.dayFormat));
33227         this.setMonth(d.format(this.monthFormat));
33228         this.setYear(d.format(this.yearFormat));
33229         
33230         this.validate();
33231         
33232         return;
33233     },
33234     
33235     setDay : function(v)
33236     {
33237         this.dayField.setValue(v);
33238         this.inputEl.dom.value = this.getValue();
33239         this.validate();
33240         return;
33241     },
33242     
33243     setMonth : function(v)
33244     {
33245         this.monthField.setValue(v, true);
33246         this.inputEl.dom.value = this.getValue();
33247         this.validate();
33248         return;
33249     },
33250     
33251     setYear : function(v)
33252     {
33253         this.yearField.setValue(v);
33254         this.inputEl.dom.value = this.getValue();
33255         this.validate();
33256         return;
33257     },
33258     
33259     getDay : function()
33260     {
33261         return this.dayField.getValue();
33262     },
33263     
33264     getMonth : function()
33265     {
33266         return this.monthField.getValue();
33267     },
33268     
33269     getYear : function()
33270     {
33271         return this.yearField.getValue();
33272     },
33273     
33274     getValue : function()
33275     {
33276         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33277         
33278         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33279         
33280         return date;
33281     },
33282     
33283     reset : function()
33284     {
33285         this.setDay('');
33286         this.setMonth('');
33287         this.setYear('');
33288         this.inputEl.dom.value = '';
33289         this.validate();
33290         return;
33291     },
33292     
33293     validate : function()
33294     {
33295         var d = this.dayField.validate();
33296         var m = this.monthField.validate();
33297         var y = this.yearField.validate();
33298         
33299         var valid = true;
33300         
33301         if(
33302                 (!this.dayAllowBlank && !d) ||
33303                 (!this.monthAllowBlank && !m) ||
33304                 (!this.yearAllowBlank && !y)
33305         ){
33306             valid = false;
33307         }
33308         
33309         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33310             return valid;
33311         }
33312         
33313         if(valid){
33314             this.markValid();
33315             return valid;
33316         }
33317         
33318         this.markInvalid();
33319         
33320         return valid;
33321     },
33322     
33323     markValid : function()
33324     {
33325         
33326         var label = this.el.select('label', true).first();
33327         var icon = this.el.select('i.fa-star', true).first();
33328
33329         if(label && icon){
33330             icon.remove();
33331         }
33332         
33333         this.fireEvent('valid', this);
33334     },
33335     
33336      /**
33337      * Mark this field as invalid
33338      * @param {String} msg The validation message
33339      */
33340     markInvalid : function(msg)
33341     {
33342         
33343         var label = this.el.select('label', true).first();
33344         var icon = this.el.select('i.fa-star', true).first();
33345
33346         if(label && !icon){
33347             this.el.select('.roo-date-split-field-label', true).createChild({
33348                 tag : 'i',
33349                 cls : 'text-danger fa fa-lg fa-star',
33350                 tooltip : 'This field is required',
33351                 style : 'margin-right:5px;'
33352             }, label, true);
33353         }
33354         
33355         this.fireEvent('invalid', this, msg);
33356     },
33357     
33358     clearInvalid : function()
33359     {
33360         var label = this.el.select('label', true).first();
33361         var icon = this.el.select('i.fa-star', true).first();
33362
33363         if(label && icon){
33364             icon.remove();
33365         }
33366         
33367         this.fireEvent('valid', this);
33368     },
33369     
33370     getName: function()
33371     {
33372         return this.name;
33373     }
33374     
33375 });
33376
33377  /**
33378  *
33379  * This is based on 
33380  * http://masonry.desandro.com
33381  *
33382  * The idea is to render all the bricks based on vertical width...
33383  *
33384  * The original code extends 'outlayer' - we might need to use that....
33385  * 
33386  */
33387
33388
33389 /**
33390  * @class Roo.bootstrap.LayoutMasonry
33391  * @extends Roo.bootstrap.Component
33392  * Bootstrap Layout Masonry class
33393  * 
33394  * @constructor
33395  * Create a new Element
33396  * @param {Object} config The config object
33397  */
33398
33399 Roo.bootstrap.LayoutMasonry = function(config){
33400     
33401     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33402     
33403     this.bricks = [];
33404     
33405     Roo.bootstrap.LayoutMasonry.register(this);
33406     
33407     this.addEvents({
33408         // raw events
33409         /**
33410          * @event layout
33411          * Fire after layout the items
33412          * @param {Roo.bootstrap.LayoutMasonry} this
33413          * @param {Roo.EventObject} e
33414          */
33415         "layout" : true
33416     });
33417     
33418 };
33419
33420 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33421     
33422     /**
33423      * @cfg {Boolean} isLayoutInstant = no animation?
33424      */   
33425     isLayoutInstant : false, // needed?
33426    
33427     /**
33428      * @cfg {Number} boxWidth  width of the columns
33429      */   
33430     boxWidth : 450,
33431     
33432       /**
33433      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33434      */   
33435     boxHeight : 0,
33436     
33437     /**
33438      * @cfg {Number} padWidth padding below box..
33439      */   
33440     padWidth : 10, 
33441     
33442     /**
33443      * @cfg {Number} gutter gutter width..
33444      */   
33445     gutter : 10,
33446     
33447      /**
33448      * @cfg {Number} maxCols maximum number of columns
33449      */   
33450     
33451     maxCols: 0,
33452     
33453     /**
33454      * @cfg {Boolean} isAutoInitial defalut true
33455      */   
33456     isAutoInitial : true, 
33457     
33458     containerWidth: 0,
33459     
33460     /**
33461      * @cfg {Boolean} isHorizontal defalut false
33462      */   
33463     isHorizontal : false, 
33464
33465     currentSize : null,
33466     
33467     tag: 'div',
33468     
33469     cls: '',
33470     
33471     bricks: null, //CompositeElement
33472     
33473     cols : 1,
33474     
33475     _isLayoutInited : false,
33476     
33477 //    isAlternative : false, // only use for vertical layout...
33478     
33479     /**
33480      * @cfg {Number} alternativePadWidth padding below box..
33481      */   
33482     alternativePadWidth : 50,
33483     
33484     selectedBrick : [],
33485     
33486     getAutoCreate : function(){
33487         
33488         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33489         
33490         var cfg = {
33491             tag: this.tag,
33492             cls: 'blog-masonary-wrapper ' + this.cls,
33493             cn : {
33494                 cls : 'mas-boxes masonary'
33495             }
33496         };
33497         
33498         return cfg;
33499     },
33500     
33501     getChildContainer: function( )
33502     {
33503         if (this.boxesEl) {
33504             return this.boxesEl;
33505         }
33506         
33507         this.boxesEl = this.el.select('.mas-boxes').first();
33508         
33509         return this.boxesEl;
33510     },
33511     
33512     
33513     initEvents : function()
33514     {
33515         var _this = this;
33516         
33517         if(this.isAutoInitial){
33518             Roo.log('hook children rendered');
33519             this.on('childrenrendered', function() {
33520                 Roo.log('children rendered');
33521                 _this.initial();
33522             } ,this);
33523         }
33524     },
33525     
33526     initial : function()
33527     {
33528         this.selectedBrick = [];
33529         
33530         this.currentSize = this.el.getBox(true);
33531         
33532         Roo.EventManager.onWindowResize(this.resize, this); 
33533
33534         if(!this.isAutoInitial){
33535             this.layout();
33536             return;
33537         }
33538         
33539         this.layout();
33540         
33541         return;
33542         //this.layout.defer(500,this);
33543         
33544     },
33545     
33546     resize : function()
33547     {
33548         var cs = this.el.getBox(true);
33549         
33550         if (
33551                 this.currentSize.width == cs.width && 
33552                 this.currentSize.x == cs.x && 
33553                 this.currentSize.height == cs.height && 
33554                 this.currentSize.y == cs.y 
33555         ) {
33556             Roo.log("no change in with or X or Y");
33557             return;
33558         }
33559         
33560         this.currentSize = cs;
33561         
33562         this.layout();
33563         
33564     },
33565     
33566     layout : function()
33567     {   
33568         this._resetLayout();
33569         
33570         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33571         
33572         this.layoutItems( isInstant );
33573       
33574         this._isLayoutInited = true;
33575         
33576         this.fireEvent('layout', this);
33577         
33578     },
33579     
33580     _resetLayout : function()
33581     {
33582         if(this.isHorizontal){
33583             this.horizontalMeasureColumns();
33584             return;
33585         }
33586         
33587         this.verticalMeasureColumns();
33588         
33589     },
33590     
33591     verticalMeasureColumns : function()
33592     {
33593         this.getContainerWidth();
33594         
33595 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33596 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33597 //            return;
33598 //        }
33599         
33600         var boxWidth = this.boxWidth + this.padWidth;
33601         
33602         if(this.containerWidth < this.boxWidth){
33603             boxWidth = this.containerWidth
33604         }
33605         
33606         var containerWidth = this.containerWidth;
33607         
33608         var cols = Math.floor(containerWidth / boxWidth);
33609         
33610         this.cols = Math.max( cols, 1 );
33611         
33612         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33613         
33614         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33615         
33616         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33617         
33618         this.colWidth = boxWidth + avail - this.padWidth;
33619         
33620         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33621         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33622     },
33623     
33624     horizontalMeasureColumns : function()
33625     {
33626         this.getContainerWidth();
33627         
33628         var boxWidth = this.boxWidth;
33629         
33630         if(this.containerWidth < boxWidth){
33631             boxWidth = this.containerWidth;
33632         }
33633         
33634         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33635         
33636         this.el.setHeight(boxWidth);
33637         
33638     },
33639     
33640     getContainerWidth : function()
33641     {
33642         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33643     },
33644     
33645     layoutItems : function( isInstant )
33646     {
33647         Roo.log(this.bricks);
33648         
33649         var items = Roo.apply([], this.bricks);
33650         
33651         if(this.isHorizontal){
33652             this._horizontalLayoutItems( items , isInstant );
33653             return;
33654         }
33655         
33656 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33657 //            this._verticalAlternativeLayoutItems( items , isInstant );
33658 //            return;
33659 //        }
33660         
33661         this._verticalLayoutItems( items , isInstant );
33662         
33663     },
33664     
33665     _verticalLayoutItems : function ( items , isInstant)
33666     {
33667         if ( !items || !items.length ) {
33668             return;
33669         }
33670         
33671         var standard = [
33672             ['xs', 'xs', 'xs', 'tall'],
33673             ['xs', 'xs', 'tall'],
33674             ['xs', 'xs', 'sm'],
33675             ['xs', 'xs', 'xs'],
33676             ['xs', 'tall'],
33677             ['xs', 'sm'],
33678             ['xs', 'xs'],
33679             ['xs'],
33680             
33681             ['sm', 'xs', 'xs'],
33682             ['sm', 'xs'],
33683             ['sm'],
33684             
33685             ['tall', 'xs', 'xs', 'xs'],
33686             ['tall', 'xs', 'xs'],
33687             ['tall', 'xs'],
33688             ['tall']
33689             
33690         ];
33691         
33692         var queue = [];
33693         
33694         var boxes = [];
33695         
33696         var box = [];
33697         
33698         Roo.each(items, function(item, k){
33699             
33700             switch (item.size) {
33701                 // these layouts take up a full box,
33702                 case 'md' :
33703                 case 'md-left' :
33704                 case 'md-right' :
33705                 case 'wide' :
33706                     
33707                     if(box.length){
33708                         boxes.push(box);
33709                         box = [];
33710                     }
33711                     
33712                     boxes.push([item]);
33713                     
33714                     break;
33715                     
33716                 case 'xs' :
33717                 case 'sm' :
33718                 case 'tall' :
33719                     
33720                     box.push(item);
33721                     
33722                     break;
33723                 default :
33724                     break;
33725                     
33726             }
33727             
33728         }, this);
33729         
33730         if(box.length){
33731             boxes.push(box);
33732             box = [];
33733         }
33734         
33735         var filterPattern = function(box, length)
33736         {
33737             if(!box.length){
33738                 return;
33739             }
33740             
33741             var match = false;
33742             
33743             var pattern = box.slice(0, length);
33744             
33745             var format = [];
33746             
33747             Roo.each(pattern, function(i){
33748                 format.push(i.size);
33749             }, this);
33750             
33751             Roo.each(standard, function(s){
33752                 
33753                 if(String(s) != String(format)){
33754                     return;
33755                 }
33756                 
33757                 match = true;
33758                 return false;
33759                 
33760             }, this);
33761             
33762             if(!match && length == 1){
33763                 return;
33764             }
33765             
33766             if(!match){
33767                 filterPattern(box, length - 1);
33768                 return;
33769             }
33770                 
33771             queue.push(pattern);
33772
33773             box = box.slice(length, box.length);
33774
33775             filterPattern(box, 4);
33776
33777             return;
33778             
33779         }
33780         
33781         Roo.each(boxes, function(box, k){
33782             
33783             if(!box.length){
33784                 return;
33785             }
33786             
33787             if(box.length == 1){
33788                 queue.push(box);
33789                 return;
33790             }
33791             
33792             filterPattern(box, 4);
33793             
33794         }, this);
33795         
33796         this._processVerticalLayoutQueue( queue, isInstant );
33797         
33798     },
33799     
33800 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33801 //    {
33802 //        if ( !items || !items.length ) {
33803 //            return;
33804 //        }
33805 //
33806 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33807 //        
33808 //    },
33809     
33810     _horizontalLayoutItems : function ( items , isInstant)
33811     {
33812         if ( !items || !items.length || items.length < 3) {
33813             return;
33814         }
33815         
33816         items.reverse();
33817         
33818         var eItems = items.slice(0, 3);
33819         
33820         items = items.slice(3, items.length);
33821         
33822         var standard = [
33823             ['xs', 'xs', 'xs', 'wide'],
33824             ['xs', 'xs', 'wide'],
33825             ['xs', 'xs', 'sm'],
33826             ['xs', 'xs', 'xs'],
33827             ['xs', 'wide'],
33828             ['xs', 'sm'],
33829             ['xs', 'xs'],
33830             ['xs'],
33831             
33832             ['sm', 'xs', 'xs'],
33833             ['sm', 'xs'],
33834             ['sm'],
33835             
33836             ['wide', 'xs', 'xs', 'xs'],
33837             ['wide', 'xs', 'xs'],
33838             ['wide', 'xs'],
33839             ['wide'],
33840             
33841             ['wide-thin']
33842         ];
33843         
33844         var queue = [];
33845         
33846         var boxes = [];
33847         
33848         var box = [];
33849         
33850         Roo.each(items, function(item, k){
33851             
33852             switch (item.size) {
33853                 case 'md' :
33854                 case 'md-left' :
33855                 case 'md-right' :
33856                 case 'tall' :
33857                     
33858                     if(box.length){
33859                         boxes.push(box);
33860                         box = [];
33861                     }
33862                     
33863                     boxes.push([item]);
33864                     
33865                     break;
33866                     
33867                 case 'xs' :
33868                 case 'sm' :
33869                 case 'wide' :
33870                 case 'wide-thin' :
33871                     
33872                     box.push(item);
33873                     
33874                     break;
33875                 default :
33876                     break;
33877                     
33878             }
33879             
33880         }, this);
33881         
33882         if(box.length){
33883             boxes.push(box);
33884             box = [];
33885         }
33886         
33887         var filterPattern = function(box, length)
33888         {
33889             if(!box.length){
33890                 return;
33891             }
33892             
33893             var match = false;
33894             
33895             var pattern = box.slice(0, length);
33896             
33897             var format = [];
33898             
33899             Roo.each(pattern, function(i){
33900                 format.push(i.size);
33901             }, this);
33902             
33903             Roo.each(standard, function(s){
33904                 
33905                 if(String(s) != String(format)){
33906                     return;
33907                 }
33908                 
33909                 match = true;
33910                 return false;
33911                 
33912             }, this);
33913             
33914             if(!match && length == 1){
33915                 return;
33916             }
33917             
33918             if(!match){
33919                 filterPattern(box, length - 1);
33920                 return;
33921             }
33922                 
33923             queue.push(pattern);
33924
33925             box = box.slice(length, box.length);
33926
33927             filterPattern(box, 4);
33928
33929             return;
33930             
33931         }
33932         
33933         Roo.each(boxes, function(box, k){
33934             
33935             if(!box.length){
33936                 return;
33937             }
33938             
33939             if(box.length == 1){
33940                 queue.push(box);
33941                 return;
33942             }
33943             
33944             filterPattern(box, 4);
33945             
33946         }, this);
33947         
33948         
33949         var prune = [];
33950         
33951         var pos = this.el.getBox(true);
33952         
33953         var minX = pos.x;
33954         
33955         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33956         
33957         var hit_end = false;
33958         
33959         Roo.each(queue, function(box){
33960             
33961             if(hit_end){
33962                 
33963                 Roo.each(box, function(b){
33964                 
33965                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33966                     b.el.hide();
33967
33968                 }, this);
33969
33970                 return;
33971             }
33972             
33973             var mx = 0;
33974             
33975             Roo.each(box, function(b){
33976                 
33977                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33978                 b.el.show();
33979
33980                 mx = Math.max(mx, b.x);
33981                 
33982             }, this);
33983             
33984             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33985             
33986             if(maxX < minX){
33987                 
33988                 Roo.each(box, function(b){
33989                 
33990                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33991                     b.el.hide();
33992                     
33993                 }, this);
33994                 
33995                 hit_end = true;
33996                 
33997                 return;
33998             }
33999             
34000             prune.push(box);
34001             
34002         }, this);
34003         
34004         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34005     },
34006     
34007     /** Sets position of item in DOM
34008     * @param {Element} item
34009     * @param {Number} x - horizontal position
34010     * @param {Number} y - vertical position
34011     * @param {Boolean} isInstant - disables transitions
34012     */
34013     _processVerticalLayoutQueue : function( queue, isInstant )
34014     {
34015         var pos = this.el.getBox(true);
34016         var x = pos.x;
34017         var y = pos.y;
34018         var maxY = [];
34019         
34020         for (var i = 0; i < this.cols; i++){
34021             maxY[i] = pos.y;
34022         }
34023         
34024         Roo.each(queue, function(box, k){
34025             
34026             var col = k % this.cols;
34027             
34028             Roo.each(box, function(b,kk){
34029                 
34030                 b.el.position('absolute');
34031                 
34032                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34033                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34034                 
34035                 if(b.size == 'md-left' || b.size == 'md-right'){
34036                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34037                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34038                 }
34039                 
34040                 b.el.setWidth(width);
34041                 b.el.setHeight(height);
34042                 // iframe?
34043                 b.el.select('iframe',true).setSize(width,height);
34044                 
34045             }, this);
34046             
34047             for (var i = 0; i < this.cols; i++){
34048                 
34049                 if(maxY[i] < maxY[col]){
34050                     col = i;
34051                     continue;
34052                 }
34053                 
34054                 col = Math.min(col, i);
34055                 
34056             }
34057             
34058             x = pos.x + col * (this.colWidth + this.padWidth);
34059             
34060             y = maxY[col];
34061             
34062             var positions = [];
34063             
34064             switch (box.length){
34065                 case 1 :
34066                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34067                     break;
34068                 case 2 :
34069                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34070                     break;
34071                 case 3 :
34072                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34073                     break;
34074                 case 4 :
34075                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34076                     break;
34077                 default :
34078                     break;
34079             }
34080             
34081             Roo.each(box, function(b,kk){
34082                 
34083                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34084                 
34085                 var sz = b.el.getSize();
34086                 
34087                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34088                 
34089             }, this);
34090             
34091         }, this);
34092         
34093         var mY = 0;
34094         
34095         for (var i = 0; i < this.cols; i++){
34096             mY = Math.max(mY, maxY[i]);
34097         }
34098         
34099         this.el.setHeight(mY - pos.y);
34100         
34101     },
34102     
34103 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34104 //    {
34105 //        var pos = this.el.getBox(true);
34106 //        var x = pos.x;
34107 //        var y = pos.y;
34108 //        var maxX = pos.right;
34109 //        
34110 //        var maxHeight = 0;
34111 //        
34112 //        Roo.each(items, function(item, k){
34113 //            
34114 //            var c = k % 2;
34115 //            
34116 //            item.el.position('absolute');
34117 //                
34118 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34119 //
34120 //            item.el.setWidth(width);
34121 //
34122 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34123 //
34124 //            item.el.setHeight(height);
34125 //            
34126 //            if(c == 0){
34127 //                item.el.setXY([x, y], isInstant ? false : true);
34128 //            } else {
34129 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34130 //            }
34131 //            
34132 //            y = y + height + this.alternativePadWidth;
34133 //            
34134 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34135 //            
34136 //        }, this);
34137 //        
34138 //        this.el.setHeight(maxHeight);
34139 //        
34140 //    },
34141     
34142     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34143     {
34144         var pos = this.el.getBox(true);
34145         
34146         var minX = pos.x;
34147         var minY = pos.y;
34148         
34149         var maxX = pos.right;
34150         
34151         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34152         
34153         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34154         
34155         Roo.each(queue, function(box, k){
34156             
34157             Roo.each(box, function(b, kk){
34158                 
34159                 b.el.position('absolute');
34160                 
34161                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34162                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34163                 
34164                 if(b.size == 'md-left' || b.size == 'md-right'){
34165                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34166                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34167                 }
34168                 
34169                 b.el.setWidth(width);
34170                 b.el.setHeight(height);
34171                 
34172             }, this);
34173             
34174             if(!box.length){
34175                 return;
34176             }
34177             
34178             var positions = [];
34179             
34180             switch (box.length){
34181                 case 1 :
34182                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34183                     break;
34184                 case 2 :
34185                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34186                     break;
34187                 case 3 :
34188                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34189                     break;
34190                 case 4 :
34191                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34192                     break;
34193                 default :
34194                     break;
34195             }
34196             
34197             Roo.each(box, function(b,kk){
34198                 
34199                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34200                 
34201                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34202                 
34203             }, this);
34204             
34205         }, this);
34206         
34207     },
34208     
34209     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34210     {
34211         Roo.each(eItems, function(b,k){
34212             
34213             b.size = (k == 0) ? 'sm' : 'xs';
34214             b.x = (k == 0) ? 2 : 1;
34215             b.y = (k == 0) ? 2 : 1;
34216             
34217             b.el.position('absolute');
34218             
34219             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34220                 
34221             b.el.setWidth(width);
34222             
34223             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34224             
34225             b.el.setHeight(height);
34226             
34227         }, this);
34228
34229         var positions = [];
34230         
34231         positions.push({
34232             x : maxX - this.unitWidth * 2 - this.gutter,
34233             y : minY
34234         });
34235         
34236         positions.push({
34237             x : maxX - this.unitWidth,
34238             y : minY + (this.unitWidth + this.gutter) * 2
34239         });
34240         
34241         positions.push({
34242             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34243             y : minY
34244         });
34245         
34246         Roo.each(eItems, function(b,k){
34247             
34248             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34249
34250         }, this);
34251         
34252     },
34253     
34254     getVerticalOneBoxColPositions : function(x, y, box)
34255     {
34256         var pos = [];
34257         
34258         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34259         
34260         if(box[0].size == 'md-left'){
34261             rand = 0;
34262         }
34263         
34264         if(box[0].size == 'md-right'){
34265             rand = 1;
34266         }
34267         
34268         pos.push({
34269             x : x + (this.unitWidth + this.gutter) * rand,
34270             y : y
34271         });
34272         
34273         return pos;
34274     },
34275     
34276     getVerticalTwoBoxColPositions : function(x, y, box)
34277     {
34278         var pos = [];
34279         
34280         if(box[0].size == 'xs'){
34281             
34282             pos.push({
34283                 x : x,
34284                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34285             });
34286
34287             pos.push({
34288                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34289                 y : y
34290             });
34291             
34292             return pos;
34293             
34294         }
34295         
34296         pos.push({
34297             x : x,
34298             y : y
34299         });
34300
34301         pos.push({
34302             x : x + (this.unitWidth + this.gutter) * 2,
34303             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34304         });
34305         
34306         return pos;
34307         
34308     },
34309     
34310     getVerticalThreeBoxColPositions : function(x, y, box)
34311     {
34312         var pos = [];
34313         
34314         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34315             
34316             pos.push({
34317                 x : x,
34318                 y : y
34319             });
34320
34321             pos.push({
34322                 x : x + (this.unitWidth + this.gutter) * 1,
34323                 y : y
34324             });
34325             
34326             pos.push({
34327                 x : x + (this.unitWidth + this.gutter) * 2,
34328                 y : y
34329             });
34330             
34331             return pos;
34332             
34333         }
34334         
34335         if(box[0].size == 'xs' && box[1].size == 'xs'){
34336             
34337             pos.push({
34338                 x : x,
34339                 y : y
34340             });
34341
34342             pos.push({
34343                 x : x,
34344                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34345             });
34346             
34347             pos.push({
34348                 x : x + (this.unitWidth + this.gutter) * 1,
34349                 y : y
34350             });
34351             
34352             return pos;
34353             
34354         }
34355         
34356         pos.push({
34357             x : x,
34358             y : y
34359         });
34360
34361         pos.push({
34362             x : x + (this.unitWidth + this.gutter) * 2,
34363             y : y
34364         });
34365
34366         pos.push({
34367             x : x + (this.unitWidth + this.gutter) * 2,
34368             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34369         });
34370             
34371         return pos;
34372         
34373     },
34374     
34375     getVerticalFourBoxColPositions : function(x, y, box)
34376     {
34377         var pos = [];
34378         
34379         if(box[0].size == 'xs'){
34380             
34381             pos.push({
34382                 x : x,
34383                 y : y
34384             });
34385
34386             pos.push({
34387                 x : x,
34388                 y : y + (this.unitHeight + this.gutter) * 1
34389             });
34390             
34391             pos.push({
34392                 x : x,
34393                 y : y + (this.unitHeight + this.gutter) * 2
34394             });
34395             
34396             pos.push({
34397                 x : x + (this.unitWidth + this.gutter) * 1,
34398                 y : y
34399             });
34400             
34401             return pos;
34402             
34403         }
34404         
34405         pos.push({
34406             x : x,
34407             y : y
34408         });
34409
34410         pos.push({
34411             x : x + (this.unitWidth + this.gutter) * 2,
34412             y : y
34413         });
34414
34415         pos.push({
34416             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34417             y : y + (this.unitHeight + this.gutter) * 1
34418         });
34419
34420         pos.push({
34421             x : x + (this.unitWidth + this.gutter) * 2,
34422             y : y + (this.unitWidth + this.gutter) * 2
34423         });
34424
34425         return pos;
34426         
34427     },
34428     
34429     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34430     {
34431         var pos = [];
34432         
34433         if(box[0].size == 'md-left'){
34434             pos.push({
34435                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34436                 y : minY
34437             });
34438             
34439             return pos;
34440         }
34441         
34442         if(box[0].size == 'md-right'){
34443             pos.push({
34444                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34445                 y : minY + (this.unitWidth + this.gutter) * 1
34446             });
34447             
34448             return pos;
34449         }
34450         
34451         var rand = Math.floor(Math.random() * (4 - box[0].y));
34452         
34453         pos.push({
34454             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34455             y : minY + (this.unitWidth + this.gutter) * rand
34456         });
34457         
34458         return pos;
34459         
34460     },
34461     
34462     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34463     {
34464         var pos = [];
34465         
34466         if(box[0].size == 'xs'){
34467             
34468             pos.push({
34469                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34470                 y : minY
34471             });
34472
34473             pos.push({
34474                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34475                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34476             });
34477             
34478             return pos;
34479             
34480         }
34481         
34482         pos.push({
34483             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34484             y : minY
34485         });
34486
34487         pos.push({
34488             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34489             y : minY + (this.unitWidth + this.gutter) * 2
34490         });
34491         
34492         return pos;
34493         
34494     },
34495     
34496     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34497     {
34498         var pos = [];
34499         
34500         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34501             
34502             pos.push({
34503                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34504                 y : minY
34505             });
34506
34507             pos.push({
34508                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34509                 y : minY + (this.unitWidth + this.gutter) * 1
34510             });
34511             
34512             pos.push({
34513                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34514                 y : minY + (this.unitWidth + this.gutter) * 2
34515             });
34516             
34517             return pos;
34518             
34519         }
34520         
34521         if(box[0].size == 'xs' && box[1].size == 'xs'){
34522             
34523             pos.push({
34524                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34525                 y : minY
34526             });
34527
34528             pos.push({
34529                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34530                 y : minY
34531             });
34532             
34533             pos.push({
34534                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34535                 y : minY + (this.unitWidth + this.gutter) * 1
34536             });
34537             
34538             return pos;
34539             
34540         }
34541         
34542         pos.push({
34543             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34544             y : minY
34545         });
34546
34547         pos.push({
34548             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34549             y : minY + (this.unitWidth + this.gutter) * 2
34550         });
34551
34552         pos.push({
34553             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34554             y : minY + (this.unitWidth + this.gutter) * 2
34555         });
34556             
34557         return pos;
34558         
34559     },
34560     
34561     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34562     {
34563         var pos = [];
34564         
34565         if(box[0].size == 'xs'){
34566             
34567             pos.push({
34568                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34569                 y : minY
34570             });
34571
34572             pos.push({
34573                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34574                 y : minY
34575             });
34576             
34577             pos.push({
34578                 x : maxX - this.unitWidth * box[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),
34579                 y : minY
34580             });
34581             
34582             pos.push({
34583                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34584                 y : minY + (this.unitWidth + this.gutter) * 1
34585             });
34586             
34587             return pos;
34588             
34589         }
34590         
34591         pos.push({
34592             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34593             y : minY
34594         });
34595         
34596         pos.push({
34597             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].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),
34603             y : minY + (this.unitWidth + this.gutter) * 2
34604         });
34605         
34606         pos.push({
34607             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),
34608             y : minY + (this.unitWidth + this.gutter) * 2
34609         });
34610
34611         return pos;
34612         
34613     },
34614     
34615     /**
34616     * remove a Masonry Brick
34617     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34618     */
34619     removeBrick : function(brick_id)
34620     {
34621         if (!brick_id) {
34622             return;
34623         }
34624         
34625         for (var i = 0; i<this.bricks.length; i++) {
34626             if (this.bricks[i].id == brick_id) {
34627                 this.bricks.splice(i,1);
34628                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34629                 this.initial();
34630             }
34631         }
34632     },
34633     
34634     /**
34635     * adds a Masonry Brick
34636     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34637     */
34638     addBrick : function(cfg)
34639     {
34640         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34641         //this.register(cn);
34642         cn.parentId = this.id;
34643         cn.render(this.el);
34644         return cn;
34645     },
34646     
34647     /**
34648     * register a Masonry Brick
34649     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34650     */
34651     
34652     register : function(brick)
34653     {
34654         this.bricks.push(brick);
34655         brick.masonryId = this.id;
34656     },
34657     
34658     /**
34659     * clear all the Masonry Brick
34660     */
34661     clearAll : function()
34662     {
34663         this.bricks = [];
34664         //this.getChildContainer().dom.innerHTML = "";
34665         this.el.dom.innerHTML = '';
34666     },
34667     
34668     getSelected : function()
34669     {
34670         if (!this.selectedBrick) {
34671             return false;
34672         }
34673         
34674         return this.selectedBrick;
34675     }
34676 });
34677
34678 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34679     
34680     groups: {},
34681      /**
34682     * register a Masonry Layout
34683     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34684     */
34685     
34686     register : function(layout)
34687     {
34688         this.groups[layout.id] = layout;
34689     },
34690     /**
34691     * fetch a  Masonry Layout based on the masonry layout ID
34692     * @param {string} the masonry layout to add
34693     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34694     */
34695     
34696     get: function(layout_id) {
34697         if (typeof(this.groups[layout_id]) == 'undefined') {
34698             return false;
34699         }
34700         return this.groups[layout_id] ;
34701     }
34702     
34703     
34704     
34705 });
34706
34707  
34708
34709  /**
34710  *
34711  * This is based on 
34712  * http://masonry.desandro.com
34713  *
34714  * The idea is to render all the bricks based on vertical width...
34715  *
34716  * The original code extends 'outlayer' - we might need to use that....
34717  * 
34718  */
34719
34720
34721 /**
34722  * @class Roo.bootstrap.LayoutMasonryAuto
34723  * @extends Roo.bootstrap.Component
34724  * Bootstrap Layout Masonry class
34725  * 
34726  * @constructor
34727  * Create a new Element
34728  * @param {Object} config The config object
34729  */
34730
34731 Roo.bootstrap.LayoutMasonryAuto = function(config){
34732     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34733 };
34734
34735 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34736     
34737       /**
34738      * @cfg {Boolean} isFitWidth  - resize the width..
34739      */   
34740     isFitWidth : false,  // options..
34741     /**
34742      * @cfg {Boolean} isOriginLeft = left align?
34743      */   
34744     isOriginLeft : true,
34745     /**
34746      * @cfg {Boolean} isOriginTop = top align?
34747      */   
34748     isOriginTop : false,
34749     /**
34750      * @cfg {Boolean} isLayoutInstant = no animation?
34751      */   
34752     isLayoutInstant : false, // needed?
34753     /**
34754      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34755      */   
34756     isResizingContainer : true,
34757     /**
34758      * @cfg {Number} columnWidth  width of the columns 
34759      */   
34760     
34761     columnWidth : 0,
34762     
34763     /**
34764      * @cfg {Number} maxCols maximum number of columns
34765      */   
34766     
34767     maxCols: 0,
34768     /**
34769      * @cfg {Number} padHeight padding below box..
34770      */   
34771     
34772     padHeight : 10, 
34773     
34774     /**
34775      * @cfg {Boolean} isAutoInitial defalut true
34776      */   
34777     
34778     isAutoInitial : true, 
34779     
34780     // private?
34781     gutter : 0,
34782     
34783     containerWidth: 0,
34784     initialColumnWidth : 0,
34785     currentSize : null,
34786     
34787     colYs : null, // array.
34788     maxY : 0,
34789     padWidth: 10,
34790     
34791     
34792     tag: 'div',
34793     cls: '',
34794     bricks: null, //CompositeElement
34795     cols : 0, // array?
34796     // element : null, // wrapped now this.el
34797     _isLayoutInited : null, 
34798     
34799     
34800     getAutoCreate : function(){
34801         
34802         var cfg = {
34803             tag: this.tag,
34804             cls: 'blog-masonary-wrapper ' + this.cls,
34805             cn : {
34806                 cls : 'mas-boxes masonary'
34807             }
34808         };
34809         
34810         return cfg;
34811     },
34812     
34813     getChildContainer: function( )
34814     {
34815         if (this.boxesEl) {
34816             return this.boxesEl;
34817         }
34818         
34819         this.boxesEl = this.el.select('.mas-boxes').first();
34820         
34821         return this.boxesEl;
34822     },
34823     
34824     
34825     initEvents : function()
34826     {
34827         var _this = this;
34828         
34829         if(this.isAutoInitial){
34830             Roo.log('hook children rendered');
34831             this.on('childrenrendered', function() {
34832                 Roo.log('children rendered');
34833                 _this.initial();
34834             } ,this);
34835         }
34836         
34837     },
34838     
34839     initial : function()
34840     {
34841         this.reloadItems();
34842
34843         this.currentSize = this.el.getBox(true);
34844
34845         /// was window resize... - let's see if this works..
34846         Roo.EventManager.onWindowResize(this.resize, this); 
34847
34848         if(!this.isAutoInitial){
34849             this.layout();
34850             return;
34851         }
34852         
34853         this.layout.defer(500,this);
34854     },
34855     
34856     reloadItems: function()
34857     {
34858         this.bricks = this.el.select('.masonry-brick', true);
34859         
34860         this.bricks.each(function(b) {
34861             //Roo.log(b.getSize());
34862             if (!b.attr('originalwidth')) {
34863                 b.attr('originalwidth',  b.getSize().width);
34864             }
34865             
34866         });
34867         
34868         Roo.log(this.bricks.elements.length);
34869     },
34870     
34871     resize : function()
34872     {
34873         Roo.log('resize');
34874         var cs = this.el.getBox(true);
34875         
34876         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34877             Roo.log("no change in with or X");
34878             return;
34879         }
34880         this.currentSize = cs;
34881         this.layout();
34882     },
34883     
34884     layout : function()
34885     {
34886          Roo.log('layout');
34887         this._resetLayout();
34888         //this._manageStamps();
34889       
34890         // don't animate first layout
34891         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34892         this.layoutItems( isInstant );
34893       
34894         // flag for initalized
34895         this._isLayoutInited = true;
34896     },
34897     
34898     layoutItems : function( isInstant )
34899     {
34900         //var items = this._getItemsForLayout( this.items );
34901         // original code supports filtering layout items.. we just ignore it..
34902         
34903         this._layoutItems( this.bricks , isInstant );
34904       
34905         this._postLayout();
34906     },
34907     _layoutItems : function ( items , isInstant)
34908     {
34909        //this.fireEvent( 'layout', this, items );
34910     
34911
34912         if ( !items || !items.elements.length ) {
34913           // no items, emit event with empty array
34914             return;
34915         }
34916
34917         var queue = [];
34918         items.each(function(item) {
34919             Roo.log("layout item");
34920             Roo.log(item);
34921             // get x/y object from method
34922             var position = this._getItemLayoutPosition( item );
34923             // enqueue
34924             position.item = item;
34925             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34926             queue.push( position );
34927         }, this);
34928       
34929         this._processLayoutQueue( queue );
34930     },
34931     /** Sets position of item in DOM
34932     * @param {Element} item
34933     * @param {Number} x - horizontal position
34934     * @param {Number} y - vertical position
34935     * @param {Boolean} isInstant - disables transitions
34936     */
34937     _processLayoutQueue : function( queue )
34938     {
34939         for ( var i=0, len = queue.length; i < len; i++ ) {
34940             var obj = queue[i];
34941             obj.item.position('absolute');
34942             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34943         }
34944     },
34945       
34946     
34947     /**
34948     * Any logic you want to do after each layout,
34949     * i.e. size the container
34950     */
34951     _postLayout : function()
34952     {
34953         this.resizeContainer();
34954     },
34955     
34956     resizeContainer : function()
34957     {
34958         if ( !this.isResizingContainer ) {
34959             return;
34960         }
34961         var size = this._getContainerSize();
34962         if ( size ) {
34963             this.el.setSize(size.width,size.height);
34964             this.boxesEl.setSize(size.width,size.height);
34965         }
34966     },
34967     
34968     
34969     
34970     _resetLayout : function()
34971     {
34972         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34973         this.colWidth = this.el.getWidth();
34974         //this.gutter = this.el.getWidth(); 
34975         
34976         this.measureColumns();
34977
34978         // reset column Y
34979         var i = this.cols;
34980         this.colYs = [];
34981         while (i--) {
34982             this.colYs.push( 0 );
34983         }
34984     
34985         this.maxY = 0;
34986     },
34987
34988     measureColumns : function()
34989     {
34990         this.getContainerWidth();
34991       // if columnWidth is 0, default to outerWidth of first item
34992         if ( !this.columnWidth ) {
34993             var firstItem = this.bricks.first();
34994             Roo.log(firstItem);
34995             this.columnWidth  = this.containerWidth;
34996             if (firstItem && firstItem.attr('originalwidth') ) {
34997                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34998             }
34999             // columnWidth fall back to item of first element
35000             Roo.log("set column width?");
35001                         this.initialColumnWidth = this.columnWidth  ;
35002
35003             // if first elem has no width, default to size of container
35004             
35005         }
35006         
35007         
35008         if (this.initialColumnWidth) {
35009             this.columnWidth = this.initialColumnWidth;
35010         }
35011         
35012         
35013             
35014         // column width is fixed at the top - however if container width get's smaller we should
35015         // reduce it...
35016         
35017         // this bit calcs how man columns..
35018             
35019         var columnWidth = this.columnWidth += this.gutter;
35020       
35021         // calculate columns
35022         var containerWidth = this.containerWidth + this.gutter;
35023         
35024         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35025         // fix rounding errors, typically with gutters
35026         var excess = columnWidth - containerWidth % columnWidth;
35027         
35028         
35029         // if overshoot is less than a pixel, round up, otherwise floor it
35030         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35031         cols = Math[ mathMethod ]( cols );
35032         this.cols = Math.max( cols, 1 );
35033         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35034         
35035          // padding positioning..
35036         var totalColWidth = this.cols * this.columnWidth;
35037         var padavail = this.containerWidth - totalColWidth;
35038         // so for 2 columns - we need 3 'pads'
35039         
35040         var padNeeded = (1+this.cols) * this.padWidth;
35041         
35042         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35043         
35044         this.columnWidth += padExtra
35045         //this.padWidth = Math.floor(padavail /  ( this.cols));
35046         
35047         // adjust colum width so that padding is fixed??
35048         
35049         // we have 3 columns ... total = width * 3
35050         // we have X left over... that should be used by 
35051         
35052         //if (this.expandC) {
35053             
35054         //}
35055         
35056         
35057         
35058     },
35059     
35060     getContainerWidth : function()
35061     {
35062        /* // container is parent if fit width
35063         var container = this.isFitWidth ? this.element.parentNode : this.element;
35064         // check that this.size and size are there
35065         // IE8 triggers resize on body size change, so they might not be
35066         
35067         var size = getSize( container );  //FIXME
35068         this.containerWidth = size && size.innerWidth; //FIXME
35069         */
35070          
35071         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35072         
35073     },
35074     
35075     _getItemLayoutPosition : function( item )  // what is item?
35076     {
35077         // we resize the item to our columnWidth..
35078       
35079         item.setWidth(this.columnWidth);
35080         item.autoBoxAdjust  = false;
35081         
35082         var sz = item.getSize();
35083  
35084         // how many columns does this brick span
35085         var remainder = this.containerWidth % this.columnWidth;
35086         
35087         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35088         // round if off by 1 pixel, otherwise use ceil
35089         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35090         colSpan = Math.min( colSpan, this.cols );
35091         
35092         // normally this should be '1' as we dont' currently allow multi width columns..
35093         
35094         var colGroup = this._getColGroup( colSpan );
35095         // get the minimum Y value from the columns
35096         var minimumY = Math.min.apply( Math, colGroup );
35097         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35098         
35099         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35100          
35101         // position the brick
35102         var position = {
35103             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35104             y: this.currentSize.y + minimumY + this.padHeight
35105         };
35106         
35107         Roo.log(position);
35108         // apply setHeight to necessary columns
35109         var setHeight = minimumY + sz.height + this.padHeight;
35110         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35111         
35112         var setSpan = this.cols + 1 - colGroup.length;
35113         for ( var i = 0; i < setSpan; i++ ) {
35114           this.colYs[ shortColIndex + i ] = setHeight ;
35115         }
35116       
35117         return position;
35118     },
35119     
35120     /**
35121      * @param {Number} colSpan - number of columns the element spans
35122      * @returns {Array} colGroup
35123      */
35124     _getColGroup : function( colSpan )
35125     {
35126         if ( colSpan < 2 ) {
35127           // if brick spans only one column, use all the column Ys
35128           return this.colYs;
35129         }
35130       
35131         var colGroup = [];
35132         // how many different places could this brick fit horizontally
35133         var groupCount = this.cols + 1 - colSpan;
35134         // for each group potential horizontal position
35135         for ( var i = 0; i < groupCount; i++ ) {
35136           // make an array of colY values for that one group
35137           var groupColYs = this.colYs.slice( i, i + colSpan );
35138           // and get the max value of the array
35139           colGroup[i] = Math.max.apply( Math, groupColYs );
35140         }
35141         return colGroup;
35142     },
35143     /*
35144     _manageStamp : function( stamp )
35145     {
35146         var stampSize =  stamp.getSize();
35147         var offset = stamp.getBox();
35148         // get the columns that this stamp affects
35149         var firstX = this.isOriginLeft ? offset.x : offset.right;
35150         var lastX = firstX + stampSize.width;
35151         var firstCol = Math.floor( firstX / this.columnWidth );
35152         firstCol = Math.max( 0, firstCol );
35153         
35154         var lastCol = Math.floor( lastX / this.columnWidth );
35155         // lastCol should not go over if multiple of columnWidth #425
35156         lastCol -= lastX % this.columnWidth ? 0 : 1;
35157         lastCol = Math.min( this.cols - 1, lastCol );
35158         
35159         // set colYs to bottom of the stamp
35160         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35161             stampSize.height;
35162             
35163         for ( var i = firstCol; i <= lastCol; i++ ) {
35164           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35165         }
35166     },
35167     */
35168     
35169     _getContainerSize : function()
35170     {
35171         this.maxY = Math.max.apply( Math, this.colYs );
35172         var size = {
35173             height: this.maxY
35174         };
35175       
35176         if ( this.isFitWidth ) {
35177             size.width = this._getContainerFitWidth();
35178         }
35179       
35180         return size;
35181     },
35182     
35183     _getContainerFitWidth : function()
35184     {
35185         var unusedCols = 0;
35186         // count unused columns
35187         var i = this.cols;
35188         while ( --i ) {
35189           if ( this.colYs[i] !== 0 ) {
35190             break;
35191           }
35192           unusedCols++;
35193         }
35194         // fit container to columns that have been used
35195         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35196     },
35197     
35198     needsResizeLayout : function()
35199     {
35200         var previousWidth = this.containerWidth;
35201         this.getContainerWidth();
35202         return previousWidth !== this.containerWidth;
35203     }
35204  
35205 });
35206
35207  
35208
35209  /*
35210  * - LGPL
35211  *
35212  * element
35213  * 
35214  */
35215
35216 /**
35217  * @class Roo.bootstrap.MasonryBrick
35218  * @extends Roo.bootstrap.Component
35219  * Bootstrap MasonryBrick class
35220  * 
35221  * @constructor
35222  * Create a new MasonryBrick
35223  * @param {Object} config The config object
35224  */
35225
35226 Roo.bootstrap.MasonryBrick = function(config){
35227     
35228     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35229     
35230     Roo.bootstrap.MasonryBrick.register(this);
35231     
35232     this.addEvents({
35233         // raw events
35234         /**
35235          * @event click
35236          * When a MasonryBrick is clcik
35237          * @param {Roo.bootstrap.MasonryBrick} this
35238          * @param {Roo.EventObject} e
35239          */
35240         "click" : true
35241     });
35242 };
35243
35244 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35245     
35246     /**
35247      * @cfg {String} title
35248      */   
35249     title : '',
35250     /**
35251      * @cfg {String} html
35252      */   
35253     html : '',
35254     /**
35255      * @cfg {String} bgimage
35256      */   
35257     bgimage : '',
35258     /**
35259      * @cfg {String} videourl
35260      */   
35261     videourl : '',
35262     /**
35263      * @cfg {String} cls
35264      */   
35265     cls : '',
35266     /**
35267      * @cfg {String} href
35268      */   
35269     href : '',
35270     /**
35271      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35272      */   
35273     size : 'xs',
35274     
35275     /**
35276      * @cfg {String} placetitle (center|bottom)
35277      */   
35278     placetitle : '',
35279     
35280     /**
35281      * @cfg {Boolean} isFitContainer defalut true
35282      */   
35283     isFitContainer : true, 
35284     
35285     /**
35286      * @cfg {Boolean} preventDefault defalut false
35287      */   
35288     preventDefault : false, 
35289     
35290     /**
35291      * @cfg {Boolean} inverse defalut false
35292      */   
35293     maskInverse : false, 
35294     
35295     getAutoCreate : function()
35296     {
35297         if(!this.isFitContainer){
35298             return this.getSplitAutoCreate();
35299         }
35300         
35301         var cls = 'masonry-brick masonry-brick-full';
35302         
35303         if(this.href.length){
35304             cls += ' masonry-brick-link';
35305         }
35306         
35307         if(this.bgimage.length){
35308             cls += ' masonry-brick-image';
35309         }
35310         
35311         if(this.maskInverse){
35312             cls += ' mask-inverse';
35313         }
35314         
35315         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35316             cls += ' enable-mask';
35317         }
35318         
35319         if(this.size){
35320             cls += ' masonry-' + this.size + '-brick';
35321         }
35322         
35323         if(this.placetitle.length){
35324             
35325             switch (this.placetitle) {
35326                 case 'center' :
35327                     cls += ' masonry-center-title';
35328                     break;
35329                 case 'bottom' :
35330                     cls += ' masonry-bottom-title';
35331                     break;
35332                 default:
35333                     break;
35334             }
35335             
35336         } else {
35337             if(!this.html.length && !this.bgimage.length){
35338                 cls += ' masonry-center-title';
35339             }
35340
35341             if(!this.html.length && this.bgimage.length){
35342                 cls += ' masonry-bottom-title';
35343             }
35344         }
35345         
35346         if(this.cls){
35347             cls += ' ' + this.cls;
35348         }
35349         
35350         var cfg = {
35351             tag: (this.href.length) ? 'a' : 'div',
35352             cls: cls,
35353             cn: [
35354                 {
35355                     tag: 'div',
35356                     cls: 'masonry-brick-mask'
35357                 },
35358                 {
35359                     tag: 'div',
35360                     cls: 'masonry-brick-paragraph',
35361                     cn: []
35362                 }
35363             ]
35364         };
35365         
35366         if(this.href.length){
35367             cfg.href = this.href;
35368         }
35369         
35370         var cn = cfg.cn[1].cn;
35371         
35372         if(this.title.length){
35373             cn.push({
35374                 tag: 'h4',
35375                 cls: 'masonry-brick-title',
35376                 html: this.title
35377             });
35378         }
35379         
35380         if(this.html.length){
35381             cn.push({
35382                 tag: 'p',
35383                 cls: 'masonry-brick-text',
35384                 html: this.html
35385             });
35386         }
35387         
35388         if (!this.title.length && !this.html.length) {
35389             cfg.cn[1].cls += ' hide';
35390         }
35391         
35392         if(this.bgimage.length){
35393             cfg.cn.push({
35394                 tag: 'img',
35395                 cls: 'masonry-brick-image-view',
35396                 src: this.bgimage
35397             });
35398         }
35399         
35400         if(this.videourl.length){
35401             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35402             // youtube support only?
35403             cfg.cn.push({
35404                 tag: 'iframe',
35405                 cls: 'masonry-brick-image-view',
35406                 src: vurl,
35407                 frameborder : 0,
35408                 allowfullscreen : true
35409             });
35410         }
35411         
35412         return cfg;
35413         
35414     },
35415     
35416     getSplitAutoCreate : function()
35417     {
35418         var cls = 'masonry-brick masonry-brick-split';
35419         
35420         if(this.href.length){
35421             cls += ' masonry-brick-link';
35422         }
35423         
35424         if(this.bgimage.length){
35425             cls += ' masonry-brick-image';
35426         }
35427         
35428         if(this.size){
35429             cls += ' masonry-' + this.size + '-brick';
35430         }
35431         
35432         switch (this.placetitle) {
35433             case 'center' :
35434                 cls += ' masonry-center-title';
35435                 break;
35436             case 'bottom' :
35437                 cls += ' masonry-bottom-title';
35438                 break;
35439             default:
35440                 if(!this.bgimage.length){
35441                     cls += ' masonry-center-title';
35442                 }
35443
35444                 if(this.bgimage.length){
35445                     cls += ' masonry-bottom-title';
35446                 }
35447                 break;
35448         }
35449         
35450         if(this.cls){
35451             cls += ' ' + this.cls;
35452         }
35453         
35454         var cfg = {
35455             tag: (this.href.length) ? 'a' : 'div',
35456             cls: cls,
35457             cn: [
35458                 {
35459                     tag: 'div',
35460                     cls: 'masonry-brick-split-head',
35461                     cn: [
35462                         {
35463                             tag: 'div',
35464                             cls: 'masonry-brick-paragraph',
35465                             cn: []
35466                         }
35467                     ]
35468                 },
35469                 {
35470                     tag: 'div',
35471                     cls: 'masonry-brick-split-body',
35472                     cn: []
35473                 }
35474             ]
35475         };
35476         
35477         if(this.href.length){
35478             cfg.href = this.href;
35479         }
35480         
35481         if(this.title.length){
35482             cfg.cn[0].cn[0].cn.push({
35483                 tag: 'h4',
35484                 cls: 'masonry-brick-title',
35485                 html: this.title
35486             });
35487         }
35488         
35489         if(this.html.length){
35490             cfg.cn[1].cn.push({
35491                 tag: 'p',
35492                 cls: 'masonry-brick-text',
35493                 html: this.html
35494             });
35495         }
35496
35497         if(this.bgimage.length){
35498             cfg.cn[0].cn.push({
35499                 tag: 'img',
35500                 cls: 'masonry-brick-image-view',
35501                 src: this.bgimage
35502             });
35503         }
35504         
35505         if(this.videourl.length){
35506             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35507             // youtube support only?
35508             cfg.cn[0].cn.cn.push({
35509                 tag: 'iframe',
35510                 cls: 'masonry-brick-image-view',
35511                 src: vurl,
35512                 frameborder : 0,
35513                 allowfullscreen : true
35514             });
35515         }
35516         
35517         return cfg;
35518     },
35519     
35520     initEvents: function() 
35521     {
35522         switch (this.size) {
35523             case 'xs' :
35524                 this.x = 1;
35525                 this.y = 1;
35526                 break;
35527             case 'sm' :
35528                 this.x = 2;
35529                 this.y = 2;
35530                 break;
35531             case 'md' :
35532             case 'md-left' :
35533             case 'md-right' :
35534                 this.x = 3;
35535                 this.y = 3;
35536                 break;
35537             case 'tall' :
35538                 this.x = 2;
35539                 this.y = 3;
35540                 break;
35541             case 'wide' :
35542                 this.x = 3;
35543                 this.y = 2;
35544                 break;
35545             case 'wide-thin' :
35546                 this.x = 3;
35547                 this.y = 1;
35548                 break;
35549                         
35550             default :
35551                 break;
35552         }
35553         
35554         if(Roo.isTouch){
35555             this.el.on('touchstart', this.onTouchStart, this);
35556             this.el.on('touchmove', this.onTouchMove, this);
35557             this.el.on('touchend', this.onTouchEnd, this);
35558             this.el.on('contextmenu', this.onContextMenu, this);
35559         } else {
35560             this.el.on('mouseenter'  ,this.enter, this);
35561             this.el.on('mouseleave', this.leave, this);
35562             this.el.on('click', this.onClick, this);
35563         }
35564         
35565         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35566             this.parent().bricks.push(this);   
35567         }
35568         
35569     },
35570     
35571     onClick: function(e, el)
35572     {
35573         var time = this.endTimer - this.startTimer;
35574         // Roo.log(e.preventDefault());
35575         if(Roo.isTouch){
35576             if(time > 1000){
35577                 e.preventDefault();
35578                 return;
35579             }
35580         }
35581         
35582         if(!this.preventDefault){
35583             return;
35584         }
35585         
35586         e.preventDefault();
35587         
35588         if (this.activeClass != '') {
35589             this.selectBrick();
35590         }
35591         
35592         this.fireEvent('click', this, e);
35593     },
35594     
35595     enter: function(e, el)
35596     {
35597         e.preventDefault();
35598         
35599         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35600             return;
35601         }
35602         
35603         if(this.bgimage.length && this.html.length){
35604             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35605         }
35606     },
35607     
35608     leave: function(e, el)
35609     {
35610         e.preventDefault();
35611         
35612         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35613             return;
35614         }
35615         
35616         if(this.bgimage.length && this.html.length){
35617             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35618         }
35619     },
35620     
35621     onTouchStart: function(e, el)
35622     {
35623 //        e.preventDefault();
35624         
35625         this.touchmoved = false;
35626         
35627         if(!this.isFitContainer){
35628             return;
35629         }
35630         
35631         if(!this.bgimage.length || !this.html.length){
35632             return;
35633         }
35634         
35635         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35636         
35637         this.timer = new Date().getTime();
35638         
35639     },
35640     
35641     onTouchMove: function(e, el)
35642     {
35643         this.touchmoved = true;
35644     },
35645     
35646     onContextMenu : function(e,el)
35647     {
35648         e.preventDefault();
35649         e.stopPropagation();
35650         return false;
35651     },
35652     
35653     onTouchEnd: function(e, el)
35654     {
35655 //        e.preventDefault();
35656         
35657         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35658         
35659             this.leave(e,el);
35660             
35661             return;
35662         }
35663         
35664         if(!this.bgimage.length || !this.html.length){
35665             
35666             if(this.href.length){
35667                 window.location.href = this.href;
35668             }
35669             
35670             return;
35671         }
35672         
35673         if(!this.isFitContainer){
35674             return;
35675         }
35676         
35677         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35678         
35679         window.location.href = this.href;
35680     },
35681     
35682     //selection on single brick only
35683     selectBrick : function() {
35684         
35685         if (!this.parentId) {
35686             return;
35687         }
35688         
35689         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35690         var index = m.selectedBrick.indexOf(this.id);
35691         
35692         if ( index > -1) {
35693             m.selectedBrick.splice(index,1);
35694             this.el.removeClass(this.activeClass);
35695             return;
35696         }
35697         
35698         for(var i = 0; i < m.selectedBrick.length; i++) {
35699             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35700             b.el.removeClass(b.activeClass);
35701         }
35702         
35703         m.selectedBrick = [];
35704         
35705         m.selectedBrick.push(this.id);
35706         this.el.addClass(this.activeClass);
35707         return;
35708     },
35709     
35710     isSelected : function(){
35711         return this.el.hasClass(this.activeClass);
35712         
35713     }
35714 });
35715
35716 Roo.apply(Roo.bootstrap.MasonryBrick, {
35717     
35718     //groups: {},
35719     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35720      /**
35721     * register a Masonry Brick
35722     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35723     */
35724     
35725     register : function(brick)
35726     {
35727         //this.groups[brick.id] = brick;
35728         this.groups.add(brick.id, brick);
35729     },
35730     /**
35731     * fetch a  masonry brick based on the masonry brick ID
35732     * @param {string} the masonry brick to add
35733     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35734     */
35735     
35736     get: function(brick_id) 
35737     {
35738         // if (typeof(this.groups[brick_id]) == 'undefined') {
35739         //     return false;
35740         // }
35741         // return this.groups[brick_id] ;
35742         
35743         if(this.groups.key(brick_id)) {
35744             return this.groups.key(brick_id);
35745         }
35746         
35747         return false;
35748     }
35749     
35750     
35751     
35752 });
35753
35754  /*
35755  * - LGPL
35756  *
35757  * element
35758  * 
35759  */
35760
35761 /**
35762  * @class Roo.bootstrap.Brick
35763  * @extends Roo.bootstrap.Component
35764  * Bootstrap Brick class
35765  * 
35766  * @constructor
35767  * Create a new Brick
35768  * @param {Object} config The config object
35769  */
35770
35771 Roo.bootstrap.Brick = function(config){
35772     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35773     
35774     this.addEvents({
35775         // raw events
35776         /**
35777          * @event click
35778          * When a Brick is click
35779          * @param {Roo.bootstrap.Brick} this
35780          * @param {Roo.EventObject} e
35781          */
35782         "click" : true
35783     });
35784 };
35785
35786 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35787     
35788     /**
35789      * @cfg {String} title
35790      */   
35791     title : '',
35792     /**
35793      * @cfg {String} html
35794      */   
35795     html : '',
35796     /**
35797      * @cfg {String} bgimage
35798      */   
35799     bgimage : '',
35800     /**
35801      * @cfg {String} cls
35802      */   
35803     cls : '',
35804     /**
35805      * @cfg {String} href
35806      */   
35807     href : '',
35808     /**
35809      * @cfg {String} video
35810      */   
35811     video : '',
35812     /**
35813      * @cfg {Boolean} square
35814      */   
35815     square : true,
35816     
35817     getAutoCreate : function()
35818     {
35819         var cls = 'roo-brick';
35820         
35821         if(this.href.length){
35822             cls += ' roo-brick-link';
35823         }
35824         
35825         if(this.bgimage.length){
35826             cls += ' roo-brick-image';
35827         }
35828         
35829         if(!this.html.length && !this.bgimage.length){
35830             cls += ' roo-brick-center-title';
35831         }
35832         
35833         if(!this.html.length && this.bgimage.length){
35834             cls += ' roo-brick-bottom-title';
35835         }
35836         
35837         if(this.cls){
35838             cls += ' ' + this.cls;
35839         }
35840         
35841         var cfg = {
35842             tag: (this.href.length) ? 'a' : 'div',
35843             cls: cls,
35844             cn: [
35845                 {
35846                     tag: 'div',
35847                     cls: 'roo-brick-paragraph',
35848                     cn: []
35849                 }
35850             ]
35851         };
35852         
35853         if(this.href.length){
35854             cfg.href = this.href;
35855         }
35856         
35857         var cn = cfg.cn[0].cn;
35858         
35859         if(this.title.length){
35860             cn.push({
35861                 tag: 'h4',
35862                 cls: 'roo-brick-title',
35863                 html: this.title
35864             });
35865         }
35866         
35867         if(this.html.length){
35868             cn.push({
35869                 tag: 'p',
35870                 cls: 'roo-brick-text',
35871                 html: this.html
35872             });
35873         } else {
35874             cn.cls += ' hide';
35875         }
35876         
35877         if(this.bgimage.length){
35878             cfg.cn.push({
35879                 tag: 'img',
35880                 cls: 'roo-brick-image-view',
35881                 src: this.bgimage
35882             });
35883         }
35884         
35885         return cfg;
35886     },
35887     
35888     initEvents: function() 
35889     {
35890         if(this.title.length || this.html.length){
35891             this.el.on('mouseenter'  ,this.enter, this);
35892             this.el.on('mouseleave', this.leave, this);
35893         }
35894         
35895         Roo.EventManager.onWindowResize(this.resize, this); 
35896         
35897         if(this.bgimage.length){
35898             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35899             this.imageEl.on('load', this.onImageLoad, this);
35900             return;
35901         }
35902         
35903         this.resize();
35904     },
35905     
35906     onImageLoad : function()
35907     {
35908         this.resize();
35909     },
35910     
35911     resize : function()
35912     {
35913         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35914         
35915         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35916         
35917         if(this.bgimage.length){
35918             var image = this.el.select('.roo-brick-image-view', true).first();
35919             
35920             image.setWidth(paragraph.getWidth());
35921             
35922             if(this.square){
35923                 image.setHeight(paragraph.getWidth());
35924             }
35925             
35926             this.el.setHeight(image.getHeight());
35927             paragraph.setHeight(image.getHeight());
35928             
35929         }
35930         
35931     },
35932     
35933     enter: function(e, el)
35934     {
35935         e.preventDefault();
35936         
35937         if(this.bgimage.length){
35938             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35939             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35940         }
35941     },
35942     
35943     leave: function(e, el)
35944     {
35945         e.preventDefault();
35946         
35947         if(this.bgimage.length){
35948             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35949             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35950         }
35951     }
35952     
35953 });
35954
35955  
35956
35957  /*
35958  * - LGPL
35959  *
35960  * Number field 
35961  */
35962
35963 /**
35964  * @class Roo.bootstrap.NumberField
35965  * @extends Roo.bootstrap.Input
35966  * Bootstrap NumberField class
35967  * 
35968  * 
35969  * 
35970  * 
35971  * @constructor
35972  * Create a new NumberField
35973  * @param {Object} config The config object
35974  */
35975
35976 Roo.bootstrap.NumberField = function(config){
35977     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35978 };
35979
35980 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35981     
35982     /**
35983      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35984      */
35985     allowDecimals : true,
35986     /**
35987      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35988      */
35989     decimalSeparator : ".",
35990     /**
35991      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35992      */
35993     decimalPrecision : 2,
35994     /**
35995      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35996      */
35997     allowNegative : true,
35998     
35999     /**
36000      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36001      */
36002     allowZero: true,
36003     /**
36004      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36005      */
36006     minValue : Number.NEGATIVE_INFINITY,
36007     /**
36008      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36009      */
36010     maxValue : Number.MAX_VALUE,
36011     /**
36012      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36013      */
36014     minText : "The minimum value for this field is {0}",
36015     /**
36016      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36017      */
36018     maxText : "The maximum value for this field is {0}",
36019     /**
36020      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36021      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36022      */
36023     nanText : "{0} is not a valid number",
36024     /**
36025      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36026      */
36027     thousandsDelimiter : false,
36028     /**
36029      * @cfg {String} valueAlign alignment of value
36030      */
36031     valueAlign : "left",
36032
36033     getAutoCreate : function()
36034     {
36035         var hiddenInput = {
36036             tag: 'input',
36037             type: 'hidden',
36038             id: Roo.id(),
36039             cls: 'hidden-number-input'
36040         };
36041         
36042         if (this.name) {
36043             hiddenInput.name = this.name;
36044         }
36045         
36046         this.name = '';
36047         
36048         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36049         
36050         this.name = hiddenInput.name;
36051         
36052         if(cfg.cn.length > 0) {
36053             cfg.cn.push(hiddenInput);
36054         }
36055         
36056         return cfg;
36057     },
36058
36059     // private
36060     initEvents : function()
36061     {   
36062         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36063         
36064         var allowed = "0123456789";
36065         
36066         if(this.allowDecimals){
36067             allowed += this.decimalSeparator;
36068         }
36069         
36070         if(this.allowNegative){
36071             allowed += "-";
36072         }
36073         
36074         if(this.thousandsDelimiter) {
36075             allowed += ",";
36076         }
36077         
36078         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36079         
36080         var keyPress = function(e){
36081             
36082             var k = e.getKey();
36083             
36084             var c = e.getCharCode();
36085             
36086             if(
36087                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36088                     allowed.indexOf(String.fromCharCode(c)) === -1
36089             ){
36090                 e.stopEvent();
36091                 return;
36092             }
36093             
36094             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36095                 return;
36096             }
36097             
36098             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36099                 e.stopEvent();
36100             }
36101         };
36102         
36103         this.el.on("keypress", keyPress, this);
36104     },
36105     
36106     validateValue : function(value)
36107     {
36108         
36109         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36110             return false;
36111         }
36112         
36113         var num = this.parseValue(value);
36114         
36115         if(isNaN(num)){
36116             this.markInvalid(String.format(this.nanText, value));
36117             return false;
36118         }
36119         
36120         if(num < this.minValue){
36121             this.markInvalid(String.format(this.minText, this.minValue));
36122             return false;
36123         }
36124         
36125         if(num > this.maxValue){
36126             this.markInvalid(String.format(this.maxText, this.maxValue));
36127             return false;
36128         }
36129         
36130         return true;
36131     },
36132
36133     getValue : function()
36134     {
36135         var v = this.hiddenEl().getValue();
36136         
36137         return this.fixPrecision(this.parseValue(v));
36138     },
36139
36140     parseValue : function(value)
36141     {
36142         if(this.thousandsDelimiter) {
36143             value += "";
36144             r = new RegExp(",", "g");
36145             value = value.replace(r, "");
36146         }
36147         
36148         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36149         return isNaN(value) ? '' : value;
36150     },
36151
36152     fixPrecision : function(value)
36153     {
36154         if(this.thousandsDelimiter) {
36155             value += "";
36156             r = new RegExp(",", "g");
36157             value = value.replace(r, "");
36158         }
36159         
36160         var nan = isNaN(value);
36161         
36162         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36163             return nan ? '' : value;
36164         }
36165         return parseFloat(value).toFixed(this.decimalPrecision);
36166     },
36167
36168     setValue : function(v)
36169     {
36170         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36171         
36172         this.value = v;
36173         
36174         if(this.rendered){
36175             
36176             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36177             
36178             this.inputEl().dom.value = (v == '') ? '' :
36179                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36180             
36181             if(!this.allowZero && v === '0') {
36182                 this.hiddenEl().dom.value = '';
36183                 this.inputEl().dom.value = '';
36184             }
36185             
36186             this.validate();
36187         }
36188     },
36189
36190     decimalPrecisionFcn : function(v)
36191     {
36192         return Math.floor(v);
36193     },
36194
36195     beforeBlur : function()
36196     {
36197         var v = this.parseValue(this.getRawValue());
36198         
36199         if(v || v === 0 || v === ''){
36200             this.setValue(v);
36201         }
36202     },
36203     
36204     hiddenEl : function()
36205     {
36206         return this.el.select('input.hidden-number-input',true).first();
36207     }
36208     
36209 });
36210
36211  
36212
36213 /*
36214 * Licence: LGPL
36215 */
36216
36217 /**
36218  * @class Roo.bootstrap.DocumentSlider
36219  * @extends Roo.bootstrap.Component
36220  * Bootstrap DocumentSlider class
36221  * 
36222  * @constructor
36223  * Create a new DocumentViewer
36224  * @param {Object} config The config object
36225  */
36226
36227 Roo.bootstrap.DocumentSlider = function(config){
36228     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36229     
36230     this.files = [];
36231     
36232     this.addEvents({
36233         /**
36234          * @event initial
36235          * Fire after initEvent
36236          * @param {Roo.bootstrap.DocumentSlider} this
36237          */
36238         "initial" : true,
36239         /**
36240          * @event update
36241          * Fire after update
36242          * @param {Roo.bootstrap.DocumentSlider} this
36243          */
36244         "update" : true,
36245         /**
36246          * @event click
36247          * Fire after click
36248          * @param {Roo.bootstrap.DocumentSlider} this
36249          */
36250         "click" : true
36251     });
36252 };
36253
36254 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36255     
36256     files : false,
36257     
36258     indicator : 0,
36259     
36260     getAutoCreate : function()
36261     {
36262         var cfg = {
36263             tag : 'div',
36264             cls : 'roo-document-slider',
36265             cn : [
36266                 {
36267                     tag : 'div',
36268                     cls : 'roo-document-slider-header',
36269                     cn : [
36270                         {
36271                             tag : 'div',
36272                             cls : 'roo-document-slider-header-title'
36273                         }
36274                     ]
36275                 },
36276                 {
36277                     tag : 'div',
36278                     cls : 'roo-document-slider-body',
36279                     cn : [
36280                         {
36281                             tag : 'div',
36282                             cls : 'roo-document-slider-prev',
36283                             cn : [
36284                                 {
36285                                     tag : 'i',
36286                                     cls : 'fa fa-chevron-left'
36287                                 }
36288                             ]
36289                         },
36290                         {
36291                             tag : 'div',
36292                             cls : 'roo-document-slider-thumb',
36293                             cn : [
36294                                 {
36295                                     tag : 'img',
36296                                     cls : 'roo-document-slider-image'
36297                                 }
36298                             ]
36299                         },
36300                         {
36301                             tag : 'div',
36302                             cls : 'roo-document-slider-next',
36303                             cn : [
36304                                 {
36305                                     tag : 'i',
36306                                     cls : 'fa fa-chevron-right'
36307                                 }
36308                             ]
36309                         }
36310                     ]
36311                 }
36312             ]
36313         };
36314         
36315         return cfg;
36316     },
36317     
36318     initEvents : function()
36319     {
36320         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36321         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36322         
36323         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36324         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36325         
36326         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36327         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36328         
36329         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36330         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36331         
36332         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36333         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36334         
36335         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36336         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36337         
36338         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36339         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36340         
36341         this.thumbEl.on('click', this.onClick, this);
36342         
36343         this.prevIndicator.on('click', this.prev, this);
36344         
36345         this.nextIndicator.on('click', this.next, this);
36346         
36347     },
36348     
36349     initial : function()
36350     {
36351         if(this.files.length){
36352             this.indicator = 1;
36353             this.update()
36354         }
36355         
36356         this.fireEvent('initial', this);
36357     },
36358     
36359     update : function()
36360     {
36361         this.imageEl.attr('src', this.files[this.indicator - 1]);
36362         
36363         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36364         
36365         this.prevIndicator.show();
36366         
36367         if(this.indicator == 1){
36368             this.prevIndicator.hide();
36369         }
36370         
36371         this.nextIndicator.show();
36372         
36373         if(this.indicator == this.files.length){
36374             this.nextIndicator.hide();
36375         }
36376         
36377         this.thumbEl.scrollTo('top');
36378         
36379         this.fireEvent('update', this);
36380     },
36381     
36382     onClick : function(e)
36383     {
36384         e.preventDefault();
36385         
36386         this.fireEvent('click', this);
36387     },
36388     
36389     prev : function(e)
36390     {
36391         e.preventDefault();
36392         
36393         this.indicator = Math.max(1, this.indicator - 1);
36394         
36395         this.update();
36396     },
36397     
36398     next : function(e)
36399     {
36400         e.preventDefault();
36401         
36402         this.indicator = Math.min(this.files.length, this.indicator + 1);
36403         
36404         this.update();
36405     }
36406 });
36407 /*
36408  * - LGPL
36409  *
36410  * RadioSet
36411  *
36412  *
36413  */
36414
36415 /**
36416  * @class Roo.bootstrap.RadioSet
36417  * @extends Roo.bootstrap.Input
36418  * Bootstrap RadioSet class
36419  * @cfg {String} indicatorpos (left|right) default left
36420  * @cfg {Boolean} inline (true|false) inline the element (default true)
36421  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36422  * @constructor
36423  * Create a new RadioSet
36424  * @param {Object} config The config object
36425  */
36426
36427 Roo.bootstrap.RadioSet = function(config){
36428     
36429     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36430     
36431     this.radioes = [];
36432     
36433     Roo.bootstrap.RadioSet.register(this);
36434     
36435     this.addEvents({
36436         /**
36437         * @event check
36438         * Fires when the element is checked or unchecked.
36439         * @param {Roo.bootstrap.RadioSet} this This radio
36440         * @param {Roo.bootstrap.Radio} item The checked item
36441         */
36442        check : true,
36443        /**
36444         * @event click
36445         * Fires when the element is click.
36446         * @param {Roo.bootstrap.RadioSet} this This radio set
36447         * @param {Roo.bootstrap.Radio} item The checked item
36448         * @param {Roo.EventObject} e The event object
36449         */
36450        click : true
36451     });
36452     
36453 };
36454
36455 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36456
36457     radioes : false,
36458     
36459     inline : true,
36460     
36461     weight : '',
36462     
36463     indicatorpos : 'left',
36464     
36465     getAutoCreate : function()
36466     {
36467         var label = {
36468             tag : 'label',
36469             cls : 'roo-radio-set-label',
36470             cn : [
36471                 {
36472                     tag : 'span',
36473                     html : this.fieldLabel
36474                 }
36475             ]
36476         };
36477         if (Roo.bootstrap.version == 3) {
36478             
36479             
36480             if(this.indicatorpos == 'left'){
36481                 label.cn.unshift({
36482                     tag : 'i',
36483                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36484                     tooltip : 'This field is required'
36485                 });
36486             } else {
36487                 label.cn.push({
36488                     tag : 'i',
36489                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36490                     tooltip : 'This field is required'
36491                 });
36492             }
36493         }
36494         var items = {
36495             tag : 'div',
36496             cls : 'roo-radio-set-items'
36497         };
36498         
36499         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36500         
36501         if (align === 'left' && this.fieldLabel.length) {
36502             
36503             items = {
36504                 cls : "roo-radio-set-right", 
36505                 cn: [
36506                     items
36507                 ]
36508             };
36509             
36510             if(this.labelWidth > 12){
36511                 label.style = "width: " + this.labelWidth + 'px';
36512             }
36513             
36514             if(this.labelWidth < 13 && this.labelmd == 0){
36515                 this.labelmd = this.labelWidth;
36516             }
36517             
36518             if(this.labellg > 0){
36519                 label.cls += ' col-lg-' + this.labellg;
36520                 items.cls += ' col-lg-' + (12 - this.labellg);
36521             }
36522             
36523             if(this.labelmd > 0){
36524                 label.cls += ' col-md-' + this.labelmd;
36525                 items.cls += ' col-md-' + (12 - this.labelmd);
36526             }
36527             
36528             if(this.labelsm > 0){
36529                 label.cls += ' col-sm-' + this.labelsm;
36530                 items.cls += ' col-sm-' + (12 - this.labelsm);
36531             }
36532             
36533             if(this.labelxs > 0){
36534                 label.cls += ' col-xs-' + this.labelxs;
36535                 items.cls += ' col-xs-' + (12 - this.labelxs);
36536             }
36537         }
36538         
36539         var cfg = {
36540             tag : 'div',
36541             cls : 'roo-radio-set',
36542             cn : [
36543                 {
36544                     tag : 'input',
36545                     cls : 'roo-radio-set-input',
36546                     type : 'hidden',
36547                     name : this.name,
36548                     value : this.value ? this.value :  ''
36549                 },
36550                 label,
36551                 items
36552             ]
36553         };
36554         
36555         if(this.weight.length){
36556             cfg.cls += ' roo-radio-' + this.weight;
36557         }
36558         
36559         if(this.inline) {
36560             cfg.cls += ' roo-radio-set-inline';
36561         }
36562         
36563         var settings=this;
36564         ['xs','sm','md','lg'].map(function(size){
36565             if (settings[size]) {
36566                 cfg.cls += ' col-' + size + '-' + settings[size];
36567             }
36568         });
36569         
36570         return cfg;
36571         
36572     },
36573
36574     initEvents : function()
36575     {
36576         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36577         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36578         
36579         if(!this.fieldLabel.length){
36580             this.labelEl.hide();
36581         }
36582         
36583         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36584         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36585         
36586         this.indicator = this.indicatorEl();
36587         
36588         if(this.indicator){
36589             this.indicator.addClass('invisible');
36590         }
36591         
36592         this.originalValue = this.getValue();
36593         
36594     },
36595     
36596     inputEl: function ()
36597     {
36598         return this.el.select('.roo-radio-set-input', true).first();
36599     },
36600     
36601     getChildContainer : function()
36602     {
36603         return this.itemsEl;
36604     },
36605     
36606     register : function(item)
36607     {
36608         this.radioes.push(item);
36609         
36610     },
36611     
36612     validate : function()
36613     {   
36614         if(this.getVisibilityEl().hasClass('hidden')){
36615             return true;
36616         }
36617         
36618         var valid = false;
36619         
36620         Roo.each(this.radioes, function(i){
36621             if(!i.checked){
36622                 return;
36623             }
36624             
36625             valid = true;
36626             return false;
36627         });
36628         
36629         if(this.allowBlank) {
36630             return true;
36631         }
36632         
36633         if(this.disabled || valid){
36634             this.markValid();
36635             return true;
36636         }
36637         
36638         this.markInvalid();
36639         return false;
36640         
36641     },
36642     
36643     markValid : function()
36644     {
36645         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36646             this.indicatorEl().removeClass('visible');
36647             this.indicatorEl().addClass('invisible');
36648         }
36649         
36650         
36651         if (Roo.bootstrap.version == 3) {
36652             this.el.removeClass([this.invalidClass, this.validClass]);
36653             this.el.addClass(this.validClass);
36654         } else {
36655             this.el.removeClass(['is-invalid','is-valid']);
36656             this.el.addClass(['is-valid']);
36657         }
36658         this.fireEvent('valid', this);
36659     },
36660     
36661     markInvalid : function(msg)
36662     {
36663         if(this.allowBlank || this.disabled){
36664             return;
36665         }
36666         
36667         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36668             this.indicatorEl().removeClass('invisible');
36669             this.indicatorEl().addClass('visible');
36670         }
36671         if (Roo.bootstrap.version == 3) {
36672             this.el.removeClass([this.invalidClass, this.validClass]);
36673             this.el.addClass(this.invalidClass);
36674         } else {
36675             this.el.removeClass(['is-invalid','is-valid']);
36676             this.el.addClass(['is-invalid']);
36677         }
36678         
36679         this.fireEvent('invalid', this, msg);
36680         
36681     },
36682     
36683     setValue : function(v, suppressEvent)
36684     {   
36685         if(this.value === v){
36686             return;
36687         }
36688         
36689         this.value = v;
36690         
36691         if(this.rendered){
36692             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36693         }
36694         
36695         Roo.each(this.radioes, function(i){
36696             i.checked = false;
36697             i.el.removeClass('checked');
36698         });
36699         
36700         Roo.each(this.radioes, function(i){
36701             
36702             if(i.value === v || i.value.toString() === v.toString()){
36703                 i.checked = true;
36704                 i.el.addClass('checked');
36705                 
36706                 if(suppressEvent !== true){
36707                     this.fireEvent('check', this, i);
36708                 }
36709                 
36710                 return false;
36711             }
36712             
36713         }, this);
36714         
36715         this.validate();
36716     },
36717     
36718     clearInvalid : function(){
36719         
36720         if(!this.el || this.preventMark){
36721             return;
36722         }
36723         
36724         this.el.removeClass([this.invalidClass]);
36725         
36726         this.fireEvent('valid', this);
36727     }
36728     
36729 });
36730
36731 Roo.apply(Roo.bootstrap.RadioSet, {
36732     
36733     groups: {},
36734     
36735     register : function(set)
36736     {
36737         this.groups[set.name] = set;
36738     },
36739     
36740     get: function(name) 
36741     {
36742         if (typeof(this.groups[name]) == 'undefined') {
36743             return false;
36744         }
36745         
36746         return this.groups[name] ;
36747     }
36748     
36749 });
36750 /*
36751  * Based on:
36752  * Ext JS Library 1.1.1
36753  * Copyright(c) 2006-2007, Ext JS, LLC.
36754  *
36755  * Originally Released Under LGPL - original licence link has changed is not relivant.
36756  *
36757  * Fork - LGPL
36758  * <script type="text/javascript">
36759  */
36760
36761
36762 /**
36763  * @class Roo.bootstrap.SplitBar
36764  * @extends Roo.util.Observable
36765  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36766  * <br><br>
36767  * Usage:
36768  * <pre><code>
36769 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36770                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36771 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36772 split.minSize = 100;
36773 split.maxSize = 600;
36774 split.animate = true;
36775 split.on('moved', splitterMoved);
36776 </code></pre>
36777  * @constructor
36778  * Create a new SplitBar
36779  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36780  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36781  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36782  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36783                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36784                         position of the SplitBar).
36785  */
36786 Roo.bootstrap.SplitBar = function(cfg){
36787     
36788     /** @private */
36789     
36790     //{
36791     //  dragElement : elm
36792     //  resizingElement: el,
36793         // optional..
36794     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36795     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36796         // existingProxy ???
36797     //}
36798     
36799     this.el = Roo.get(cfg.dragElement, true);
36800     this.el.dom.unselectable = "on";
36801     /** @private */
36802     this.resizingEl = Roo.get(cfg.resizingElement, true);
36803
36804     /**
36805      * @private
36806      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36807      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36808      * @type Number
36809      */
36810     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36811     
36812     /**
36813      * The minimum size of the resizing element. (Defaults to 0)
36814      * @type Number
36815      */
36816     this.minSize = 0;
36817     
36818     /**
36819      * The maximum size of the resizing element. (Defaults to 2000)
36820      * @type Number
36821      */
36822     this.maxSize = 2000;
36823     
36824     /**
36825      * Whether to animate the transition to the new size
36826      * @type Boolean
36827      */
36828     this.animate = false;
36829     
36830     /**
36831      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36832      * @type Boolean
36833      */
36834     this.useShim = false;
36835     
36836     /** @private */
36837     this.shim = null;
36838     
36839     if(!cfg.existingProxy){
36840         /** @private */
36841         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36842     }else{
36843         this.proxy = Roo.get(cfg.existingProxy).dom;
36844     }
36845     /** @private */
36846     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36847     
36848     /** @private */
36849     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36850     
36851     /** @private */
36852     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36853     
36854     /** @private */
36855     this.dragSpecs = {};
36856     
36857     /**
36858      * @private The adapter to use to positon and resize elements
36859      */
36860     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36861     this.adapter.init(this);
36862     
36863     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36864         /** @private */
36865         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36866         this.el.addClass("roo-splitbar-h");
36867     }else{
36868         /** @private */
36869         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36870         this.el.addClass("roo-splitbar-v");
36871     }
36872     
36873     this.addEvents({
36874         /**
36875          * @event resize
36876          * Fires when the splitter is moved (alias for {@link #event-moved})
36877          * @param {Roo.bootstrap.SplitBar} this
36878          * @param {Number} newSize the new width or height
36879          */
36880         "resize" : true,
36881         /**
36882          * @event moved
36883          * Fires when the splitter is moved
36884          * @param {Roo.bootstrap.SplitBar} this
36885          * @param {Number} newSize the new width or height
36886          */
36887         "moved" : true,
36888         /**
36889          * @event beforeresize
36890          * Fires before the splitter is dragged
36891          * @param {Roo.bootstrap.SplitBar} this
36892          */
36893         "beforeresize" : true,
36894
36895         "beforeapply" : true
36896     });
36897
36898     Roo.util.Observable.call(this);
36899 };
36900
36901 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36902     onStartProxyDrag : function(x, y){
36903         this.fireEvent("beforeresize", this);
36904         if(!this.overlay){
36905             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36906             o.unselectable();
36907             o.enableDisplayMode("block");
36908             // all splitbars share the same overlay
36909             Roo.bootstrap.SplitBar.prototype.overlay = o;
36910         }
36911         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36912         this.overlay.show();
36913         Roo.get(this.proxy).setDisplayed("block");
36914         var size = this.adapter.getElementSize(this);
36915         this.activeMinSize = this.getMinimumSize();;
36916         this.activeMaxSize = this.getMaximumSize();;
36917         var c1 = size - this.activeMinSize;
36918         var c2 = Math.max(this.activeMaxSize - size, 0);
36919         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36920             this.dd.resetConstraints();
36921             this.dd.setXConstraint(
36922                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36923                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36924             );
36925             this.dd.setYConstraint(0, 0);
36926         }else{
36927             this.dd.resetConstraints();
36928             this.dd.setXConstraint(0, 0);
36929             this.dd.setYConstraint(
36930                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
36931                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36932             );
36933          }
36934         this.dragSpecs.startSize = size;
36935         this.dragSpecs.startPoint = [x, y];
36936         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36937     },
36938     
36939     /** 
36940      * @private Called after the drag operation by the DDProxy
36941      */
36942     onEndProxyDrag : function(e){
36943         Roo.get(this.proxy).setDisplayed(false);
36944         var endPoint = Roo.lib.Event.getXY(e);
36945         if(this.overlay){
36946             this.overlay.hide();
36947         }
36948         var newSize;
36949         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36950             newSize = this.dragSpecs.startSize + 
36951                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36952                     endPoint[0] - this.dragSpecs.startPoint[0] :
36953                     this.dragSpecs.startPoint[0] - endPoint[0]
36954                 );
36955         }else{
36956             newSize = this.dragSpecs.startSize + 
36957                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36958                     endPoint[1] - this.dragSpecs.startPoint[1] :
36959                     this.dragSpecs.startPoint[1] - endPoint[1]
36960                 );
36961         }
36962         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36963         if(newSize != this.dragSpecs.startSize){
36964             if(this.fireEvent('beforeapply', this, newSize) !== false){
36965                 this.adapter.setElementSize(this, newSize);
36966                 this.fireEvent("moved", this, newSize);
36967                 this.fireEvent("resize", this, newSize);
36968             }
36969         }
36970     },
36971     
36972     /**
36973      * Get the adapter this SplitBar uses
36974      * @return The adapter object
36975      */
36976     getAdapter : function(){
36977         return this.adapter;
36978     },
36979     
36980     /**
36981      * Set the adapter this SplitBar uses
36982      * @param {Object} adapter A SplitBar adapter object
36983      */
36984     setAdapter : function(adapter){
36985         this.adapter = adapter;
36986         this.adapter.init(this);
36987     },
36988     
36989     /**
36990      * Gets the minimum size for the resizing element
36991      * @return {Number} The minimum size
36992      */
36993     getMinimumSize : function(){
36994         return this.minSize;
36995     },
36996     
36997     /**
36998      * Sets the minimum size for the resizing element
36999      * @param {Number} minSize The minimum size
37000      */
37001     setMinimumSize : function(minSize){
37002         this.minSize = minSize;
37003     },
37004     
37005     /**
37006      * Gets the maximum size for the resizing element
37007      * @return {Number} The maximum size
37008      */
37009     getMaximumSize : function(){
37010         return this.maxSize;
37011     },
37012     
37013     /**
37014      * Sets the maximum size for the resizing element
37015      * @param {Number} maxSize The maximum size
37016      */
37017     setMaximumSize : function(maxSize){
37018         this.maxSize = maxSize;
37019     },
37020     
37021     /**
37022      * Sets the initialize size for the resizing element
37023      * @param {Number} size The initial size
37024      */
37025     setCurrentSize : function(size){
37026         var oldAnimate = this.animate;
37027         this.animate = false;
37028         this.adapter.setElementSize(this, size);
37029         this.animate = oldAnimate;
37030     },
37031     
37032     /**
37033      * Destroy this splitbar. 
37034      * @param {Boolean} removeEl True to remove the element
37035      */
37036     destroy : function(removeEl){
37037         if(this.shim){
37038             this.shim.remove();
37039         }
37040         this.dd.unreg();
37041         this.proxy.parentNode.removeChild(this.proxy);
37042         if(removeEl){
37043             this.el.remove();
37044         }
37045     }
37046 });
37047
37048 /**
37049  * @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.
37050  */
37051 Roo.bootstrap.SplitBar.createProxy = function(dir){
37052     var proxy = new Roo.Element(document.createElement("div"));
37053     proxy.unselectable();
37054     var cls = 'roo-splitbar-proxy';
37055     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37056     document.body.appendChild(proxy.dom);
37057     return proxy.dom;
37058 };
37059
37060 /** 
37061  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37062  * Default Adapter. It assumes the splitter and resizing element are not positioned
37063  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37064  */
37065 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37066 };
37067
37068 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37069     // do nothing for now
37070     init : function(s){
37071     
37072     },
37073     /**
37074      * Called before drag operations to get the current size of the resizing element. 
37075      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37076      */
37077      getElementSize : function(s){
37078         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37079             return s.resizingEl.getWidth();
37080         }else{
37081             return s.resizingEl.getHeight();
37082         }
37083     },
37084     
37085     /**
37086      * Called after drag operations to set the size of the resizing element.
37087      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37088      * @param {Number} newSize The new size to set
37089      * @param {Function} onComplete A function to be invoked when resizing is complete
37090      */
37091     setElementSize : function(s, newSize, onComplete){
37092         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37093             if(!s.animate){
37094                 s.resizingEl.setWidth(newSize);
37095                 if(onComplete){
37096                     onComplete(s, newSize);
37097                 }
37098             }else{
37099                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37100             }
37101         }else{
37102             
37103             if(!s.animate){
37104                 s.resizingEl.setHeight(newSize);
37105                 if(onComplete){
37106                     onComplete(s, newSize);
37107                 }
37108             }else{
37109                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37110             }
37111         }
37112     }
37113 };
37114
37115 /** 
37116  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37117  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37118  * Adapter that  moves the splitter element to align with the resized sizing element. 
37119  * Used with an absolute positioned SplitBar.
37120  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37121  * document.body, make sure you assign an id to the body element.
37122  */
37123 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37124     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37125     this.container = Roo.get(container);
37126 };
37127
37128 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37129     init : function(s){
37130         this.basic.init(s);
37131     },
37132     
37133     getElementSize : function(s){
37134         return this.basic.getElementSize(s);
37135     },
37136     
37137     setElementSize : function(s, newSize, onComplete){
37138         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37139     },
37140     
37141     moveSplitter : function(s){
37142         var yes = Roo.bootstrap.SplitBar;
37143         switch(s.placement){
37144             case yes.LEFT:
37145                 s.el.setX(s.resizingEl.getRight());
37146                 break;
37147             case yes.RIGHT:
37148                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37149                 break;
37150             case yes.TOP:
37151                 s.el.setY(s.resizingEl.getBottom());
37152                 break;
37153             case yes.BOTTOM:
37154                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37155                 break;
37156         }
37157     }
37158 };
37159
37160 /**
37161  * Orientation constant - Create a vertical SplitBar
37162  * @static
37163  * @type Number
37164  */
37165 Roo.bootstrap.SplitBar.VERTICAL = 1;
37166
37167 /**
37168  * Orientation constant - Create a horizontal SplitBar
37169  * @static
37170  * @type Number
37171  */
37172 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37173
37174 /**
37175  * Placement constant - The resizing element is to the left of the splitter element
37176  * @static
37177  * @type Number
37178  */
37179 Roo.bootstrap.SplitBar.LEFT = 1;
37180
37181 /**
37182  * Placement constant - The resizing element is to the right of the splitter element
37183  * @static
37184  * @type Number
37185  */
37186 Roo.bootstrap.SplitBar.RIGHT = 2;
37187
37188 /**
37189  * Placement constant - The resizing element is positioned above the splitter element
37190  * @static
37191  * @type Number
37192  */
37193 Roo.bootstrap.SplitBar.TOP = 3;
37194
37195 /**
37196  * Placement constant - The resizing element is positioned under splitter element
37197  * @static
37198  * @type Number
37199  */
37200 Roo.bootstrap.SplitBar.BOTTOM = 4;
37201 Roo.namespace("Roo.bootstrap.layout");/*
37202  * Based on:
37203  * Ext JS Library 1.1.1
37204  * Copyright(c) 2006-2007, Ext JS, LLC.
37205  *
37206  * Originally Released Under LGPL - original licence link has changed is not relivant.
37207  *
37208  * Fork - LGPL
37209  * <script type="text/javascript">
37210  */
37211
37212 /**
37213  * @class Roo.bootstrap.layout.Manager
37214  * @extends Roo.bootstrap.Component
37215  * Base class for layout managers.
37216  */
37217 Roo.bootstrap.layout.Manager = function(config)
37218 {
37219     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37220
37221
37222
37223
37224
37225     /** false to disable window resize monitoring @type Boolean */
37226     this.monitorWindowResize = true;
37227     this.regions = {};
37228     this.addEvents({
37229         /**
37230          * @event layout
37231          * Fires when a layout is performed.
37232          * @param {Roo.LayoutManager} this
37233          */
37234         "layout" : true,
37235         /**
37236          * @event regionresized
37237          * Fires when the user resizes a region.
37238          * @param {Roo.LayoutRegion} region The resized region
37239          * @param {Number} newSize The new size (width for east/west, height for north/south)
37240          */
37241         "regionresized" : true,
37242         /**
37243          * @event regioncollapsed
37244          * Fires when a region is collapsed.
37245          * @param {Roo.LayoutRegion} region The collapsed region
37246          */
37247         "regioncollapsed" : true,
37248         /**
37249          * @event regionexpanded
37250          * Fires when a region is expanded.
37251          * @param {Roo.LayoutRegion} region The expanded region
37252          */
37253         "regionexpanded" : true
37254     });
37255     this.updating = false;
37256
37257     if (config.el) {
37258         this.el = Roo.get(config.el);
37259         this.initEvents();
37260     }
37261
37262 };
37263
37264 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37265
37266
37267     regions : null,
37268
37269     monitorWindowResize : true,
37270
37271
37272     updating : false,
37273
37274
37275     onRender : function(ct, position)
37276     {
37277         if(!this.el){
37278             this.el = Roo.get(ct);
37279             this.initEvents();
37280         }
37281         //this.fireEvent('render',this);
37282     },
37283
37284
37285     initEvents: function()
37286     {
37287
37288
37289         // ie scrollbar fix
37290         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37291             document.body.scroll = "no";
37292         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37293             this.el.position('relative');
37294         }
37295         this.id = this.el.id;
37296         this.el.addClass("roo-layout-container");
37297         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37298         if(this.el.dom != document.body ) {
37299             this.el.on('resize', this.layout,this);
37300             this.el.on('show', this.layout,this);
37301         }
37302
37303     },
37304
37305     /**
37306      * Returns true if this layout is currently being updated
37307      * @return {Boolean}
37308      */
37309     isUpdating : function(){
37310         return this.updating;
37311     },
37312
37313     /**
37314      * Suspend the LayoutManager from doing auto-layouts while
37315      * making multiple add or remove calls
37316      */
37317     beginUpdate : function(){
37318         this.updating = true;
37319     },
37320
37321     /**
37322      * Restore auto-layouts and optionally disable the manager from performing a layout
37323      * @param {Boolean} noLayout true to disable a layout update
37324      */
37325     endUpdate : function(noLayout){
37326         this.updating = false;
37327         if(!noLayout){
37328             this.layout();
37329         }
37330     },
37331
37332     layout: function(){
37333         // abstract...
37334     },
37335
37336     onRegionResized : function(region, newSize){
37337         this.fireEvent("regionresized", region, newSize);
37338         this.layout();
37339     },
37340
37341     onRegionCollapsed : function(region){
37342         this.fireEvent("regioncollapsed", region);
37343     },
37344
37345     onRegionExpanded : function(region){
37346         this.fireEvent("regionexpanded", region);
37347     },
37348
37349     /**
37350      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37351      * performs box-model adjustments.
37352      * @return {Object} The size as an object {width: (the width), height: (the height)}
37353      */
37354     getViewSize : function()
37355     {
37356         var size;
37357         if(this.el.dom != document.body){
37358             size = this.el.getSize();
37359         }else{
37360             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37361         }
37362         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37363         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37364         return size;
37365     },
37366
37367     /**
37368      * Returns the Element this layout is bound to.
37369      * @return {Roo.Element}
37370      */
37371     getEl : function(){
37372         return this.el;
37373     },
37374
37375     /**
37376      * Returns the specified region.
37377      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37378      * @return {Roo.LayoutRegion}
37379      */
37380     getRegion : function(target){
37381         return this.regions[target.toLowerCase()];
37382     },
37383
37384     onWindowResize : function(){
37385         if(this.monitorWindowResize){
37386             this.layout();
37387         }
37388     }
37389 });
37390 /*
37391  * Based on:
37392  * Ext JS Library 1.1.1
37393  * Copyright(c) 2006-2007, Ext JS, LLC.
37394  *
37395  * Originally Released Under LGPL - original licence link has changed is not relivant.
37396  *
37397  * Fork - LGPL
37398  * <script type="text/javascript">
37399  */
37400 /**
37401  * @class Roo.bootstrap.layout.Border
37402  * @extends Roo.bootstrap.layout.Manager
37403  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37404  * please see: examples/bootstrap/nested.html<br><br>
37405  
37406 <b>The container the layout is rendered into can be either the body element or any other element.
37407 If it is not the body element, the container needs to either be an absolute positioned element,
37408 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37409 the container size if it is not the body element.</b>
37410
37411 * @constructor
37412 * Create a new Border
37413 * @param {Object} config Configuration options
37414  */
37415 Roo.bootstrap.layout.Border = function(config){
37416     config = config || {};
37417     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37418     
37419     
37420     
37421     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37422         if(config[region]){
37423             config[region].region = region;
37424             this.addRegion(config[region]);
37425         }
37426     },this);
37427     
37428 };
37429
37430 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
37431
37432 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37433     
37434     parent : false, // this might point to a 'nest' or a ???
37435     
37436     /**
37437      * Creates and adds a new region if it doesn't already exist.
37438      * @param {String} target The target region key (north, south, east, west or center).
37439      * @param {Object} config The regions config object
37440      * @return {BorderLayoutRegion} The new region
37441      */
37442     addRegion : function(config)
37443     {
37444         if(!this.regions[config.region]){
37445             var r = this.factory(config);
37446             this.bindRegion(r);
37447         }
37448         return this.regions[config.region];
37449     },
37450
37451     // private (kinda)
37452     bindRegion : function(r){
37453         this.regions[r.config.region] = r;
37454         
37455         r.on("visibilitychange",    this.layout, this);
37456         r.on("paneladded",          this.layout, this);
37457         r.on("panelremoved",        this.layout, this);
37458         r.on("invalidated",         this.layout, this);
37459         r.on("resized",             this.onRegionResized, this);
37460         r.on("collapsed",           this.onRegionCollapsed, this);
37461         r.on("expanded",            this.onRegionExpanded, this);
37462     },
37463
37464     /**
37465      * Performs a layout update.
37466      */
37467     layout : function()
37468     {
37469         if(this.updating) {
37470             return;
37471         }
37472         
37473         // render all the rebions if they have not been done alreayd?
37474         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37475             if(this.regions[region] && !this.regions[region].bodyEl){
37476                 this.regions[region].onRender(this.el)
37477             }
37478         },this);
37479         
37480         var size = this.getViewSize();
37481         var w = size.width;
37482         var h = size.height;
37483         var centerW = w;
37484         var centerH = h;
37485         var centerY = 0;
37486         var centerX = 0;
37487         //var x = 0, y = 0;
37488
37489         var rs = this.regions;
37490         var north = rs["north"];
37491         var south = rs["south"]; 
37492         var west = rs["west"];
37493         var east = rs["east"];
37494         var center = rs["center"];
37495         //if(this.hideOnLayout){ // not supported anymore
37496             //c.el.setStyle("display", "none");
37497         //}
37498         if(north && north.isVisible()){
37499             var b = north.getBox();
37500             var m = north.getMargins();
37501             b.width = w - (m.left+m.right);
37502             b.x = m.left;
37503             b.y = m.top;
37504             centerY = b.height + b.y + m.bottom;
37505             centerH -= centerY;
37506             north.updateBox(this.safeBox(b));
37507         }
37508         if(south && south.isVisible()){
37509             var b = south.getBox();
37510             var m = south.getMargins();
37511             b.width = w - (m.left+m.right);
37512             b.x = m.left;
37513             var totalHeight = (b.height + m.top + m.bottom);
37514             b.y = h - totalHeight + m.top;
37515             centerH -= totalHeight;
37516             south.updateBox(this.safeBox(b));
37517         }
37518         if(west && west.isVisible()){
37519             var b = west.getBox();
37520             var m = west.getMargins();
37521             b.height = centerH - (m.top+m.bottom);
37522             b.x = m.left;
37523             b.y = centerY + m.top;
37524             var totalWidth = (b.width + m.left + m.right);
37525             centerX += totalWidth;
37526             centerW -= totalWidth;
37527             west.updateBox(this.safeBox(b));
37528         }
37529         if(east && east.isVisible()){
37530             var b = east.getBox();
37531             var m = east.getMargins();
37532             b.height = centerH - (m.top+m.bottom);
37533             var totalWidth = (b.width + m.left + m.right);
37534             b.x = w - totalWidth + m.left;
37535             b.y = centerY + m.top;
37536             centerW -= totalWidth;
37537             east.updateBox(this.safeBox(b));
37538         }
37539         if(center){
37540             var m = center.getMargins();
37541             var centerBox = {
37542                 x: centerX + m.left,
37543                 y: centerY + m.top,
37544                 width: centerW - (m.left+m.right),
37545                 height: centerH - (m.top+m.bottom)
37546             };
37547             //if(this.hideOnLayout){
37548                 //center.el.setStyle("display", "block");
37549             //}
37550             center.updateBox(this.safeBox(centerBox));
37551         }
37552         this.el.repaint();
37553         this.fireEvent("layout", this);
37554     },
37555
37556     // private
37557     safeBox : function(box){
37558         box.width = Math.max(0, box.width);
37559         box.height = Math.max(0, box.height);
37560         return box;
37561     },
37562
37563     /**
37564      * Adds a ContentPanel (or subclass) to this layout.
37565      * @param {String} target The target region key (north, south, east, west or center).
37566      * @param {Roo.ContentPanel} panel The panel to add
37567      * @return {Roo.ContentPanel} The added panel
37568      */
37569     add : function(target, panel){
37570          
37571         target = target.toLowerCase();
37572         return this.regions[target].add(panel);
37573     },
37574
37575     /**
37576      * Remove a ContentPanel (or subclass) to this layout.
37577      * @param {String} target The target region key (north, south, east, west or center).
37578      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37579      * @return {Roo.ContentPanel} The removed panel
37580      */
37581     remove : function(target, panel){
37582         target = target.toLowerCase();
37583         return this.regions[target].remove(panel);
37584     },
37585
37586     /**
37587      * Searches all regions for a panel with the specified id
37588      * @param {String} panelId
37589      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37590      */
37591     findPanel : function(panelId){
37592         var rs = this.regions;
37593         for(var target in rs){
37594             if(typeof rs[target] != "function"){
37595                 var p = rs[target].getPanel(panelId);
37596                 if(p){
37597                     return p;
37598                 }
37599             }
37600         }
37601         return null;
37602     },
37603
37604     /**
37605      * Searches all regions for a panel with the specified id and activates (shows) it.
37606      * @param {String/ContentPanel} panelId The panels id or the panel itself
37607      * @return {Roo.ContentPanel} The shown panel or null
37608      */
37609     showPanel : function(panelId) {
37610       var rs = this.regions;
37611       for(var target in rs){
37612          var r = rs[target];
37613          if(typeof r != "function"){
37614             if(r.hasPanel(panelId)){
37615                return r.showPanel(panelId);
37616             }
37617          }
37618       }
37619       return null;
37620    },
37621
37622    /**
37623      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37624      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37625      */
37626    /*
37627     restoreState : function(provider){
37628         if(!provider){
37629             provider = Roo.state.Manager;
37630         }
37631         var sm = new Roo.LayoutStateManager();
37632         sm.init(this, provider);
37633     },
37634 */
37635  
37636  
37637     /**
37638      * Adds a xtype elements to the layout.
37639      * <pre><code>
37640
37641 layout.addxtype({
37642        xtype : 'ContentPanel',
37643        region: 'west',
37644        items: [ .... ]
37645    }
37646 );
37647
37648 layout.addxtype({
37649         xtype : 'NestedLayoutPanel',
37650         region: 'west',
37651         layout: {
37652            center: { },
37653            west: { }   
37654         },
37655         items : [ ... list of content panels or nested layout panels.. ]
37656    }
37657 );
37658 </code></pre>
37659      * @param {Object} cfg Xtype definition of item to add.
37660      */
37661     addxtype : function(cfg)
37662     {
37663         // basically accepts a pannel...
37664         // can accept a layout region..!?!?
37665         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37666         
37667         
37668         // theory?  children can only be panels??
37669         
37670         //if (!cfg.xtype.match(/Panel$/)) {
37671         //    return false;
37672         //}
37673         var ret = false;
37674         
37675         if (typeof(cfg.region) == 'undefined') {
37676             Roo.log("Failed to add Panel, region was not set");
37677             Roo.log(cfg);
37678             return false;
37679         }
37680         var region = cfg.region;
37681         delete cfg.region;
37682         
37683           
37684         var xitems = [];
37685         if (cfg.items) {
37686             xitems = cfg.items;
37687             delete cfg.items;
37688         }
37689         var nb = false;
37690         
37691         if ( region == 'center') {
37692             Roo.log("Center: " + cfg.title);
37693         }
37694         
37695         
37696         switch(cfg.xtype) 
37697         {
37698             case 'Content':  // ContentPanel (el, cfg)
37699             case 'Scroll':  // ContentPanel (el, cfg)
37700             case 'View': 
37701                 cfg.autoCreate = cfg.autoCreate || true;
37702                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37703                 //} else {
37704                 //    var el = this.el.createChild();
37705                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37706                 //}
37707                 
37708                 this.add(region, ret);
37709                 break;
37710             
37711             /*
37712             case 'TreePanel': // our new panel!
37713                 cfg.el = this.el.createChild();
37714                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37715                 this.add(region, ret);
37716                 break;
37717             */
37718             
37719             case 'Nest': 
37720                 // create a new Layout (which is  a Border Layout...
37721                 
37722                 var clayout = cfg.layout;
37723                 clayout.el  = this.el.createChild();
37724                 clayout.items   = clayout.items  || [];
37725                 
37726                 delete cfg.layout;
37727                 
37728                 // replace this exitems with the clayout ones..
37729                 xitems = clayout.items;
37730                  
37731                 // force background off if it's in center...
37732                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37733                     cfg.background = false;
37734                 }
37735                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37736                 
37737                 
37738                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37739                 //console.log('adding nested layout panel '  + cfg.toSource());
37740                 this.add(region, ret);
37741                 nb = {}; /// find first...
37742                 break;
37743             
37744             case 'Grid':
37745                 
37746                 // needs grid and region
37747                 
37748                 //var el = this.getRegion(region).el.createChild();
37749                 /*
37750                  *var el = this.el.createChild();
37751                 // create the grid first...
37752                 cfg.grid.container = el;
37753                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37754                 */
37755                 
37756                 if (region == 'center' && this.active ) {
37757                     cfg.background = false;
37758                 }
37759                 
37760                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37761                 
37762                 this.add(region, ret);
37763                 /*
37764                 if (cfg.background) {
37765                     // render grid on panel activation (if panel background)
37766                     ret.on('activate', function(gp) {
37767                         if (!gp.grid.rendered) {
37768                     //        gp.grid.render(el);
37769                         }
37770                     });
37771                 } else {
37772                   //  cfg.grid.render(el);
37773                 }
37774                 */
37775                 break;
37776            
37777            
37778             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37779                 // it was the old xcomponent building that caused this before.
37780                 // espeically if border is the top element in the tree.
37781                 ret = this;
37782                 break; 
37783                 
37784                     
37785                 
37786                 
37787                 
37788             default:
37789                 /*
37790                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37791                     
37792                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37793                     this.add(region, ret);
37794                 } else {
37795                 */
37796                     Roo.log(cfg);
37797                     throw "Can not add '" + cfg.xtype + "' to Border";
37798                     return null;
37799              
37800                                 
37801              
37802         }
37803         this.beginUpdate();
37804         // add children..
37805         var region = '';
37806         var abn = {};
37807         Roo.each(xitems, function(i)  {
37808             region = nb && i.region ? i.region : false;
37809             
37810             var add = ret.addxtype(i);
37811            
37812             if (region) {
37813                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37814                 if (!i.background) {
37815                     abn[region] = nb[region] ;
37816                 }
37817             }
37818             
37819         });
37820         this.endUpdate();
37821
37822         // make the last non-background panel active..
37823         //if (nb) { Roo.log(abn); }
37824         if (nb) {
37825             
37826             for(var r in abn) {
37827                 region = this.getRegion(r);
37828                 if (region) {
37829                     // tried using nb[r], but it does not work..
37830                      
37831                     region.showPanel(abn[r]);
37832                    
37833                 }
37834             }
37835         }
37836         return ret;
37837         
37838     },
37839     
37840     
37841 // private
37842     factory : function(cfg)
37843     {
37844         
37845         var validRegions = Roo.bootstrap.layout.Border.regions;
37846
37847         var target = cfg.region;
37848         cfg.mgr = this;
37849         
37850         var r = Roo.bootstrap.layout;
37851         Roo.log(target);
37852         switch(target){
37853             case "north":
37854                 return new r.North(cfg);
37855             case "south":
37856                 return new r.South(cfg);
37857             case "east":
37858                 return new r.East(cfg);
37859             case "west":
37860                 return new r.West(cfg);
37861             case "center":
37862                 return new r.Center(cfg);
37863         }
37864         throw 'Layout region "'+target+'" not supported.';
37865     }
37866     
37867     
37868 });
37869  /*
37870  * Based on:
37871  * Ext JS Library 1.1.1
37872  * Copyright(c) 2006-2007, Ext JS, LLC.
37873  *
37874  * Originally Released Under LGPL - original licence link has changed is not relivant.
37875  *
37876  * Fork - LGPL
37877  * <script type="text/javascript">
37878  */
37879  
37880 /**
37881  * @class Roo.bootstrap.layout.Basic
37882  * @extends Roo.util.Observable
37883  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37884  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37885  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37886  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37887  * @cfg {string}   region  the region that it inhabits..
37888  * @cfg {bool}   skipConfig skip config?
37889  * 
37890
37891  */
37892 Roo.bootstrap.layout.Basic = function(config){
37893     
37894     this.mgr = config.mgr;
37895     
37896     this.position = config.region;
37897     
37898     var skipConfig = config.skipConfig;
37899     
37900     this.events = {
37901         /**
37902          * @scope Roo.BasicLayoutRegion
37903          */
37904         
37905         /**
37906          * @event beforeremove
37907          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37908          * @param {Roo.LayoutRegion} this
37909          * @param {Roo.ContentPanel} panel The panel
37910          * @param {Object} e The cancel event object
37911          */
37912         "beforeremove" : true,
37913         /**
37914          * @event invalidated
37915          * Fires when the layout for this region is changed.
37916          * @param {Roo.LayoutRegion} this
37917          */
37918         "invalidated" : true,
37919         /**
37920          * @event visibilitychange
37921          * Fires when this region is shown or hidden 
37922          * @param {Roo.LayoutRegion} this
37923          * @param {Boolean} visibility true or false
37924          */
37925         "visibilitychange" : true,
37926         /**
37927          * @event paneladded
37928          * Fires when a panel is added. 
37929          * @param {Roo.LayoutRegion} this
37930          * @param {Roo.ContentPanel} panel The panel
37931          */
37932         "paneladded" : true,
37933         /**
37934          * @event panelremoved
37935          * Fires when a panel is removed. 
37936          * @param {Roo.LayoutRegion} this
37937          * @param {Roo.ContentPanel} panel The panel
37938          */
37939         "panelremoved" : true,
37940         /**
37941          * @event beforecollapse
37942          * Fires when this region before collapse.
37943          * @param {Roo.LayoutRegion} this
37944          */
37945         "beforecollapse" : true,
37946         /**
37947          * @event collapsed
37948          * Fires when this region is collapsed.
37949          * @param {Roo.LayoutRegion} this
37950          */
37951         "collapsed" : true,
37952         /**
37953          * @event expanded
37954          * Fires when this region is expanded.
37955          * @param {Roo.LayoutRegion} this
37956          */
37957         "expanded" : true,
37958         /**
37959          * @event slideshow
37960          * Fires when this region is slid into view.
37961          * @param {Roo.LayoutRegion} this
37962          */
37963         "slideshow" : true,
37964         /**
37965          * @event slidehide
37966          * Fires when this region slides out of view. 
37967          * @param {Roo.LayoutRegion} this
37968          */
37969         "slidehide" : true,
37970         /**
37971          * @event panelactivated
37972          * Fires when a panel is activated. 
37973          * @param {Roo.LayoutRegion} this
37974          * @param {Roo.ContentPanel} panel The activated panel
37975          */
37976         "panelactivated" : true,
37977         /**
37978          * @event resized
37979          * Fires when the user resizes this region. 
37980          * @param {Roo.LayoutRegion} this
37981          * @param {Number} newSize The new size (width for east/west, height for north/south)
37982          */
37983         "resized" : true
37984     };
37985     /** A collection of panels in this region. @type Roo.util.MixedCollection */
37986     this.panels = new Roo.util.MixedCollection();
37987     this.panels.getKey = this.getPanelId.createDelegate(this);
37988     this.box = null;
37989     this.activePanel = null;
37990     // ensure listeners are added...
37991     
37992     if (config.listeners || config.events) {
37993         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37994             listeners : config.listeners || {},
37995             events : config.events || {}
37996         });
37997     }
37998     
37999     if(skipConfig !== true){
38000         this.applyConfig(config);
38001     }
38002 };
38003
38004 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38005 {
38006     getPanelId : function(p){
38007         return p.getId();
38008     },
38009     
38010     applyConfig : function(config){
38011         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38012         this.config = config;
38013         
38014     },
38015     
38016     /**
38017      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38018      * the width, for horizontal (north, south) the height.
38019      * @param {Number} newSize The new width or height
38020      */
38021     resizeTo : function(newSize){
38022         var el = this.el ? this.el :
38023                  (this.activePanel ? this.activePanel.getEl() : null);
38024         if(el){
38025             switch(this.position){
38026                 case "east":
38027                 case "west":
38028                     el.setWidth(newSize);
38029                     this.fireEvent("resized", this, newSize);
38030                 break;
38031                 case "north":
38032                 case "south":
38033                     el.setHeight(newSize);
38034                     this.fireEvent("resized", this, newSize);
38035                 break;                
38036             }
38037         }
38038     },
38039     
38040     getBox : function(){
38041         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38042     },
38043     
38044     getMargins : function(){
38045         return this.margins;
38046     },
38047     
38048     updateBox : function(box){
38049         this.box = box;
38050         var el = this.activePanel.getEl();
38051         el.dom.style.left = box.x + "px";
38052         el.dom.style.top = box.y + "px";
38053         this.activePanel.setSize(box.width, box.height);
38054     },
38055     
38056     /**
38057      * Returns the container element for this region.
38058      * @return {Roo.Element}
38059      */
38060     getEl : function(){
38061         return this.activePanel;
38062     },
38063     
38064     /**
38065      * Returns true if this region is currently visible.
38066      * @return {Boolean}
38067      */
38068     isVisible : function(){
38069         return this.activePanel ? true : false;
38070     },
38071     
38072     setActivePanel : function(panel){
38073         panel = this.getPanel(panel);
38074         if(this.activePanel && this.activePanel != panel){
38075             this.activePanel.setActiveState(false);
38076             this.activePanel.getEl().setLeftTop(-10000,-10000);
38077         }
38078         this.activePanel = panel;
38079         panel.setActiveState(true);
38080         if(this.box){
38081             panel.setSize(this.box.width, this.box.height);
38082         }
38083         this.fireEvent("panelactivated", this, panel);
38084         this.fireEvent("invalidated");
38085     },
38086     
38087     /**
38088      * Show the specified panel.
38089      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38090      * @return {Roo.ContentPanel} The shown panel or null
38091      */
38092     showPanel : function(panel){
38093         panel = this.getPanel(panel);
38094         if(panel){
38095             this.setActivePanel(panel);
38096         }
38097         return panel;
38098     },
38099     
38100     /**
38101      * Get the active panel for this region.
38102      * @return {Roo.ContentPanel} The active panel or null
38103      */
38104     getActivePanel : function(){
38105         return this.activePanel;
38106     },
38107     
38108     /**
38109      * Add the passed ContentPanel(s)
38110      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38111      * @return {Roo.ContentPanel} The panel added (if only one was added)
38112      */
38113     add : function(panel){
38114         if(arguments.length > 1){
38115             for(var i = 0, len = arguments.length; i < len; i++) {
38116                 this.add(arguments[i]);
38117             }
38118             return null;
38119         }
38120         if(this.hasPanel(panel)){
38121             this.showPanel(panel);
38122             return panel;
38123         }
38124         var el = panel.getEl();
38125         if(el.dom.parentNode != this.mgr.el.dom){
38126             this.mgr.el.dom.appendChild(el.dom);
38127         }
38128         if(panel.setRegion){
38129             panel.setRegion(this);
38130         }
38131         this.panels.add(panel);
38132         el.setStyle("position", "absolute");
38133         if(!panel.background){
38134             this.setActivePanel(panel);
38135             if(this.config.initialSize && this.panels.getCount()==1){
38136                 this.resizeTo(this.config.initialSize);
38137             }
38138         }
38139         this.fireEvent("paneladded", this, panel);
38140         return panel;
38141     },
38142     
38143     /**
38144      * Returns true if the panel is in this region.
38145      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38146      * @return {Boolean}
38147      */
38148     hasPanel : function(panel){
38149         if(typeof panel == "object"){ // must be panel obj
38150             panel = panel.getId();
38151         }
38152         return this.getPanel(panel) ? true : false;
38153     },
38154     
38155     /**
38156      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38157      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38158      * @param {Boolean} preservePanel Overrides the config preservePanel option
38159      * @return {Roo.ContentPanel} The panel that was removed
38160      */
38161     remove : function(panel, preservePanel){
38162         panel = this.getPanel(panel);
38163         if(!panel){
38164             return null;
38165         }
38166         var e = {};
38167         this.fireEvent("beforeremove", this, panel, e);
38168         if(e.cancel === true){
38169             return null;
38170         }
38171         var panelId = panel.getId();
38172         this.panels.removeKey(panelId);
38173         return panel;
38174     },
38175     
38176     /**
38177      * Returns the panel specified or null if it's not in this region.
38178      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38179      * @return {Roo.ContentPanel}
38180      */
38181     getPanel : function(id){
38182         if(typeof id == "object"){ // must be panel obj
38183             return id;
38184         }
38185         return this.panels.get(id);
38186     },
38187     
38188     /**
38189      * Returns this regions position (north/south/east/west/center).
38190      * @return {String} 
38191      */
38192     getPosition: function(){
38193         return this.position;    
38194     }
38195 });/*
38196  * Based on:
38197  * Ext JS Library 1.1.1
38198  * Copyright(c) 2006-2007, Ext JS, LLC.
38199  *
38200  * Originally Released Under LGPL - original licence link has changed is not relivant.
38201  *
38202  * Fork - LGPL
38203  * <script type="text/javascript">
38204  */
38205  
38206 /**
38207  * @class Roo.bootstrap.layout.Region
38208  * @extends Roo.bootstrap.layout.Basic
38209  * This class represents a region in a layout manager.
38210  
38211  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38212  * @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})
38213  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38214  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38215  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38216  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38217  * @cfg {String}    title           The title for the region (overrides panel titles)
38218  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38219  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38220  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38221  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38222  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38223  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38224  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38225  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38226  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38227  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38228
38229  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38230  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38231  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38232  * @cfg {Number}    width           For East/West panels
38233  * @cfg {Number}    height          For North/South panels
38234  * @cfg {Boolean}   split           To show the splitter
38235  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38236  * 
38237  * @cfg {string}   cls             Extra CSS classes to add to region
38238  * 
38239  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38240  * @cfg {string}   region  the region that it inhabits..
38241  *
38242
38243  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38244  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38245
38246  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38247  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38248  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38249  */
38250 Roo.bootstrap.layout.Region = function(config)
38251 {
38252     this.applyConfig(config);
38253
38254     var mgr = config.mgr;
38255     var pos = config.region;
38256     config.skipConfig = true;
38257     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38258     
38259     if (mgr.el) {
38260         this.onRender(mgr.el);   
38261     }
38262      
38263     this.visible = true;
38264     this.collapsed = false;
38265     this.unrendered_panels = [];
38266 };
38267
38268 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38269
38270     position: '', // set by wrapper (eg. north/south etc..)
38271     unrendered_panels : null,  // unrendered panels.
38272     
38273     tabPosition : false,
38274     
38275     mgr: false, // points to 'Border'
38276     
38277     
38278     createBody : function(){
38279         /** This region's body element 
38280         * @type Roo.Element */
38281         this.bodyEl = this.el.createChild({
38282                 tag: "div",
38283                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38284         });
38285     },
38286
38287     onRender: function(ctr, pos)
38288     {
38289         var dh = Roo.DomHelper;
38290         /** This region's container element 
38291         * @type Roo.Element */
38292         this.el = dh.append(ctr.dom, {
38293                 tag: "div",
38294                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38295             }, true);
38296         /** This region's title element 
38297         * @type Roo.Element */
38298     
38299         this.titleEl = dh.append(this.el.dom,  {
38300                 tag: "div",
38301                 unselectable: "on",
38302                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38303                 children:[
38304                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38305                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38306                 ]
38307             }, true);
38308         
38309         this.titleEl.enableDisplayMode();
38310         /** This region's title text element 
38311         * @type HTMLElement */
38312         this.titleTextEl = this.titleEl.dom.firstChild;
38313         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38314         /*
38315         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38316         this.closeBtn.enableDisplayMode();
38317         this.closeBtn.on("click", this.closeClicked, this);
38318         this.closeBtn.hide();
38319     */
38320         this.createBody(this.config);
38321         if(this.config.hideWhenEmpty){
38322             this.hide();
38323             this.on("paneladded", this.validateVisibility, this);
38324             this.on("panelremoved", this.validateVisibility, this);
38325         }
38326         if(this.autoScroll){
38327             this.bodyEl.setStyle("overflow", "auto");
38328         }else{
38329             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38330         }
38331         //if(c.titlebar !== false){
38332             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38333                 this.titleEl.hide();
38334             }else{
38335                 this.titleEl.show();
38336                 if(this.config.title){
38337                     this.titleTextEl.innerHTML = this.config.title;
38338                 }
38339             }
38340         //}
38341         if(this.config.collapsed){
38342             this.collapse(true);
38343         }
38344         if(this.config.hidden){
38345             this.hide();
38346         }
38347         
38348         if (this.unrendered_panels && this.unrendered_panels.length) {
38349             for (var i =0;i< this.unrendered_panels.length; i++) {
38350                 this.add(this.unrendered_panels[i]);
38351             }
38352             this.unrendered_panels = null;
38353             
38354         }
38355         
38356     },
38357     
38358     applyConfig : function(c)
38359     {
38360         /*
38361          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38362             var dh = Roo.DomHelper;
38363             if(c.titlebar !== false){
38364                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38365                 this.collapseBtn.on("click", this.collapse, this);
38366                 this.collapseBtn.enableDisplayMode();
38367                 /*
38368                 if(c.showPin === true || this.showPin){
38369                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38370                     this.stickBtn.enableDisplayMode();
38371                     this.stickBtn.on("click", this.expand, this);
38372                     this.stickBtn.hide();
38373                 }
38374                 
38375             }
38376             */
38377             /** This region's collapsed element
38378             * @type Roo.Element */
38379             /*
38380              *
38381             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38382                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38383             ]}, true);
38384             
38385             if(c.floatable !== false){
38386                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38387                this.collapsedEl.on("click", this.collapseClick, this);
38388             }
38389
38390             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38391                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38392                    id: "message", unselectable: "on", style:{"float":"left"}});
38393                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38394              }
38395             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38396             this.expandBtn.on("click", this.expand, this);
38397             
38398         }
38399         
38400         if(this.collapseBtn){
38401             this.collapseBtn.setVisible(c.collapsible == true);
38402         }
38403         
38404         this.cmargins = c.cmargins || this.cmargins ||
38405                          (this.position == "west" || this.position == "east" ?
38406                              {top: 0, left: 2, right:2, bottom: 0} :
38407                              {top: 2, left: 0, right:0, bottom: 2});
38408         */
38409         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38410         
38411         
38412         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38413         
38414         this.autoScroll = c.autoScroll || false;
38415         
38416         
38417        
38418         
38419         this.duration = c.duration || .30;
38420         this.slideDuration = c.slideDuration || .45;
38421         this.config = c;
38422        
38423     },
38424     /**
38425      * Returns true if this region is currently visible.
38426      * @return {Boolean}
38427      */
38428     isVisible : function(){
38429         return this.visible;
38430     },
38431
38432     /**
38433      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38434      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38435      */
38436     //setCollapsedTitle : function(title){
38437     //    title = title || "&#160;";
38438      //   if(this.collapsedTitleTextEl){
38439       //      this.collapsedTitleTextEl.innerHTML = title;
38440        // }
38441     //},
38442
38443     getBox : function(){
38444         var b;
38445       //  if(!this.collapsed){
38446             b = this.el.getBox(false, true);
38447        // }else{
38448           //  b = this.collapsedEl.getBox(false, true);
38449         //}
38450         return b;
38451     },
38452
38453     getMargins : function(){
38454         return this.margins;
38455         //return this.collapsed ? this.cmargins : this.margins;
38456     },
38457 /*
38458     highlight : function(){
38459         this.el.addClass("x-layout-panel-dragover");
38460     },
38461
38462     unhighlight : function(){
38463         this.el.removeClass("x-layout-panel-dragover");
38464     },
38465 */
38466     updateBox : function(box)
38467     {
38468         if (!this.bodyEl) {
38469             return; // not rendered yet..
38470         }
38471         
38472         this.box = box;
38473         if(!this.collapsed){
38474             this.el.dom.style.left = box.x + "px";
38475             this.el.dom.style.top = box.y + "px";
38476             this.updateBody(box.width, box.height);
38477         }else{
38478             this.collapsedEl.dom.style.left = box.x + "px";
38479             this.collapsedEl.dom.style.top = box.y + "px";
38480             this.collapsedEl.setSize(box.width, box.height);
38481         }
38482         if(this.tabs){
38483             this.tabs.autoSizeTabs();
38484         }
38485     },
38486
38487     updateBody : function(w, h)
38488     {
38489         if(w !== null){
38490             this.el.setWidth(w);
38491             w -= this.el.getBorderWidth("rl");
38492             if(this.config.adjustments){
38493                 w += this.config.adjustments[0];
38494             }
38495         }
38496         if(h !== null && h > 0){
38497             this.el.setHeight(h);
38498             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38499             h -= this.el.getBorderWidth("tb");
38500             if(this.config.adjustments){
38501                 h += this.config.adjustments[1];
38502             }
38503             this.bodyEl.setHeight(h);
38504             if(this.tabs){
38505                 h = this.tabs.syncHeight(h);
38506             }
38507         }
38508         if(this.panelSize){
38509             w = w !== null ? w : this.panelSize.width;
38510             h = h !== null ? h : this.panelSize.height;
38511         }
38512         if(this.activePanel){
38513             var el = this.activePanel.getEl();
38514             w = w !== null ? w : el.getWidth();
38515             h = h !== null ? h : el.getHeight();
38516             this.panelSize = {width: w, height: h};
38517             this.activePanel.setSize(w, h);
38518         }
38519         if(Roo.isIE && this.tabs){
38520             this.tabs.el.repaint();
38521         }
38522     },
38523
38524     /**
38525      * Returns the container element for this region.
38526      * @return {Roo.Element}
38527      */
38528     getEl : function(){
38529         return this.el;
38530     },
38531
38532     /**
38533      * Hides this region.
38534      */
38535     hide : function(){
38536         //if(!this.collapsed){
38537             this.el.dom.style.left = "-2000px";
38538             this.el.hide();
38539         //}else{
38540          //   this.collapsedEl.dom.style.left = "-2000px";
38541          //   this.collapsedEl.hide();
38542        // }
38543         this.visible = false;
38544         this.fireEvent("visibilitychange", this, false);
38545     },
38546
38547     /**
38548      * Shows this region if it was previously hidden.
38549      */
38550     show : function(){
38551         //if(!this.collapsed){
38552             this.el.show();
38553         //}else{
38554         //    this.collapsedEl.show();
38555        // }
38556         this.visible = true;
38557         this.fireEvent("visibilitychange", this, true);
38558     },
38559 /*
38560     closeClicked : function(){
38561         if(this.activePanel){
38562             this.remove(this.activePanel);
38563         }
38564     },
38565
38566     collapseClick : function(e){
38567         if(this.isSlid){
38568            e.stopPropagation();
38569            this.slideIn();
38570         }else{
38571            e.stopPropagation();
38572            this.slideOut();
38573         }
38574     },
38575 */
38576     /**
38577      * Collapses this region.
38578      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38579      */
38580     /*
38581     collapse : function(skipAnim, skipCheck = false){
38582         if(this.collapsed) {
38583             return;
38584         }
38585         
38586         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38587             
38588             this.collapsed = true;
38589             if(this.split){
38590                 this.split.el.hide();
38591             }
38592             if(this.config.animate && skipAnim !== true){
38593                 this.fireEvent("invalidated", this);
38594                 this.animateCollapse();
38595             }else{
38596                 this.el.setLocation(-20000,-20000);
38597                 this.el.hide();
38598                 this.collapsedEl.show();
38599                 this.fireEvent("collapsed", this);
38600                 this.fireEvent("invalidated", this);
38601             }
38602         }
38603         
38604     },
38605 */
38606     animateCollapse : function(){
38607         // overridden
38608     },
38609
38610     /**
38611      * Expands this region if it was previously collapsed.
38612      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38613      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38614      */
38615     /*
38616     expand : function(e, skipAnim){
38617         if(e) {
38618             e.stopPropagation();
38619         }
38620         if(!this.collapsed || this.el.hasActiveFx()) {
38621             return;
38622         }
38623         if(this.isSlid){
38624             this.afterSlideIn();
38625             skipAnim = true;
38626         }
38627         this.collapsed = false;
38628         if(this.config.animate && skipAnim !== true){
38629             this.animateExpand();
38630         }else{
38631             this.el.show();
38632             if(this.split){
38633                 this.split.el.show();
38634             }
38635             this.collapsedEl.setLocation(-2000,-2000);
38636             this.collapsedEl.hide();
38637             this.fireEvent("invalidated", this);
38638             this.fireEvent("expanded", this);
38639         }
38640     },
38641 */
38642     animateExpand : function(){
38643         // overridden
38644     },
38645
38646     initTabs : function()
38647     {
38648         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38649         
38650         var ts = new Roo.bootstrap.panel.Tabs({
38651             el: this.bodyEl.dom,
38652             region : this,
38653             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38654             disableTooltips: this.config.disableTabTips,
38655             toolbar : this.config.toolbar
38656         });
38657         
38658         if(this.config.hideTabs){
38659             ts.stripWrap.setDisplayed(false);
38660         }
38661         this.tabs = ts;
38662         ts.resizeTabs = this.config.resizeTabs === true;
38663         ts.minTabWidth = this.config.minTabWidth || 40;
38664         ts.maxTabWidth = this.config.maxTabWidth || 250;
38665         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38666         ts.monitorResize = false;
38667         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38668         ts.bodyEl.addClass('roo-layout-tabs-body');
38669         this.panels.each(this.initPanelAsTab, this);
38670     },
38671
38672     initPanelAsTab : function(panel){
38673         var ti = this.tabs.addTab(
38674             panel.getEl().id,
38675             panel.getTitle(),
38676             null,
38677             this.config.closeOnTab && panel.isClosable(),
38678             panel.tpl
38679         );
38680         if(panel.tabTip !== undefined){
38681             ti.setTooltip(panel.tabTip);
38682         }
38683         ti.on("activate", function(){
38684               this.setActivePanel(panel);
38685         }, this);
38686         
38687         if(this.config.closeOnTab){
38688             ti.on("beforeclose", function(t, e){
38689                 e.cancel = true;
38690                 this.remove(panel);
38691             }, this);
38692         }
38693         
38694         panel.tabItem = ti;
38695         
38696         return ti;
38697     },
38698
38699     updatePanelTitle : function(panel, title)
38700     {
38701         if(this.activePanel == panel){
38702             this.updateTitle(title);
38703         }
38704         if(this.tabs){
38705             var ti = this.tabs.getTab(panel.getEl().id);
38706             ti.setText(title);
38707             if(panel.tabTip !== undefined){
38708                 ti.setTooltip(panel.tabTip);
38709             }
38710         }
38711     },
38712
38713     updateTitle : function(title){
38714         if(this.titleTextEl && !this.config.title){
38715             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38716         }
38717     },
38718
38719     setActivePanel : function(panel)
38720     {
38721         panel = this.getPanel(panel);
38722         if(this.activePanel && this.activePanel != panel){
38723             if(this.activePanel.setActiveState(false) === false){
38724                 return;
38725             }
38726         }
38727         this.activePanel = panel;
38728         panel.setActiveState(true);
38729         if(this.panelSize){
38730             panel.setSize(this.panelSize.width, this.panelSize.height);
38731         }
38732         if(this.closeBtn){
38733             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38734         }
38735         this.updateTitle(panel.getTitle());
38736         if(this.tabs){
38737             this.fireEvent("invalidated", this);
38738         }
38739         this.fireEvent("panelactivated", this, panel);
38740     },
38741
38742     /**
38743      * Shows the specified panel.
38744      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38745      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38746      */
38747     showPanel : function(panel)
38748     {
38749         panel = this.getPanel(panel);
38750         if(panel){
38751             if(this.tabs){
38752                 var tab = this.tabs.getTab(panel.getEl().id);
38753                 if(tab.isHidden()){
38754                     this.tabs.unhideTab(tab.id);
38755                 }
38756                 tab.activate();
38757             }else{
38758                 this.setActivePanel(panel);
38759             }
38760         }
38761         return panel;
38762     },
38763
38764     /**
38765      * Get the active panel for this region.
38766      * @return {Roo.ContentPanel} The active panel or null
38767      */
38768     getActivePanel : function(){
38769         return this.activePanel;
38770     },
38771
38772     validateVisibility : function(){
38773         if(this.panels.getCount() < 1){
38774             this.updateTitle("&#160;");
38775             this.closeBtn.hide();
38776             this.hide();
38777         }else{
38778             if(!this.isVisible()){
38779                 this.show();
38780             }
38781         }
38782     },
38783
38784     /**
38785      * Adds the passed ContentPanel(s) to this region.
38786      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38787      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38788      */
38789     add : function(panel)
38790     {
38791         if(arguments.length > 1){
38792             for(var i = 0, len = arguments.length; i < len; i++) {
38793                 this.add(arguments[i]);
38794             }
38795             return null;
38796         }
38797         
38798         // if we have not been rendered yet, then we can not really do much of this..
38799         if (!this.bodyEl) {
38800             this.unrendered_panels.push(panel);
38801             return panel;
38802         }
38803         
38804         
38805         
38806         
38807         if(this.hasPanel(panel)){
38808             this.showPanel(panel);
38809             return panel;
38810         }
38811         panel.setRegion(this);
38812         this.panels.add(panel);
38813        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38814             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38815             // and hide them... ???
38816             this.bodyEl.dom.appendChild(panel.getEl().dom);
38817             if(panel.background !== true){
38818                 this.setActivePanel(panel);
38819             }
38820             this.fireEvent("paneladded", this, panel);
38821             return panel;
38822         }
38823         */
38824         if(!this.tabs){
38825             this.initTabs();
38826         }else{
38827             this.initPanelAsTab(panel);
38828         }
38829         
38830         
38831         if(panel.background !== true){
38832             this.tabs.activate(panel.getEl().id);
38833         }
38834         this.fireEvent("paneladded", this, panel);
38835         return panel;
38836     },
38837
38838     /**
38839      * Hides the tab for the specified panel.
38840      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38841      */
38842     hidePanel : function(panel){
38843         if(this.tabs && (panel = this.getPanel(panel))){
38844             this.tabs.hideTab(panel.getEl().id);
38845         }
38846     },
38847
38848     /**
38849      * Unhides the tab for a previously hidden panel.
38850      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38851      */
38852     unhidePanel : function(panel){
38853         if(this.tabs && (panel = this.getPanel(panel))){
38854             this.tabs.unhideTab(panel.getEl().id);
38855         }
38856     },
38857
38858     clearPanels : function(){
38859         while(this.panels.getCount() > 0){
38860              this.remove(this.panels.first());
38861         }
38862     },
38863
38864     /**
38865      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38866      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38867      * @param {Boolean} preservePanel Overrides the config preservePanel option
38868      * @return {Roo.ContentPanel} The panel that was removed
38869      */
38870     remove : function(panel, preservePanel)
38871     {
38872         panel = this.getPanel(panel);
38873         if(!panel){
38874             return null;
38875         }
38876         var e = {};
38877         this.fireEvent("beforeremove", this, panel, e);
38878         if(e.cancel === true){
38879             return null;
38880         }
38881         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38882         var panelId = panel.getId();
38883         this.panels.removeKey(panelId);
38884         if(preservePanel){
38885             document.body.appendChild(panel.getEl().dom);
38886         }
38887         if(this.tabs){
38888             this.tabs.removeTab(panel.getEl().id);
38889         }else if (!preservePanel){
38890             this.bodyEl.dom.removeChild(panel.getEl().dom);
38891         }
38892         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38893             var p = this.panels.first();
38894             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38895             tempEl.appendChild(p.getEl().dom);
38896             this.bodyEl.update("");
38897             this.bodyEl.dom.appendChild(p.getEl().dom);
38898             tempEl = null;
38899             this.updateTitle(p.getTitle());
38900             this.tabs = null;
38901             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38902             this.setActivePanel(p);
38903         }
38904         panel.setRegion(null);
38905         if(this.activePanel == panel){
38906             this.activePanel = null;
38907         }
38908         if(this.config.autoDestroy !== false && preservePanel !== true){
38909             try{panel.destroy();}catch(e){}
38910         }
38911         this.fireEvent("panelremoved", this, panel);
38912         return panel;
38913     },
38914
38915     /**
38916      * Returns the TabPanel component used by this region
38917      * @return {Roo.TabPanel}
38918      */
38919     getTabs : function(){
38920         return this.tabs;
38921     },
38922
38923     createTool : function(parentEl, className){
38924         var btn = Roo.DomHelper.append(parentEl, {
38925             tag: "div",
38926             cls: "x-layout-tools-button",
38927             children: [ {
38928                 tag: "div",
38929                 cls: "roo-layout-tools-button-inner " + className,
38930                 html: "&#160;"
38931             }]
38932         }, true);
38933         btn.addClassOnOver("roo-layout-tools-button-over");
38934         return btn;
38935     }
38936 });/*
38937  * Based on:
38938  * Ext JS Library 1.1.1
38939  * Copyright(c) 2006-2007, Ext JS, LLC.
38940  *
38941  * Originally Released Under LGPL - original licence link has changed is not relivant.
38942  *
38943  * Fork - LGPL
38944  * <script type="text/javascript">
38945  */
38946  
38947
38948
38949 /**
38950  * @class Roo.SplitLayoutRegion
38951  * @extends Roo.LayoutRegion
38952  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38953  */
38954 Roo.bootstrap.layout.Split = function(config){
38955     this.cursor = config.cursor;
38956     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38957 };
38958
38959 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38960 {
38961     splitTip : "Drag to resize.",
38962     collapsibleSplitTip : "Drag to resize. Double click to hide.",
38963     useSplitTips : false,
38964
38965     applyConfig : function(config){
38966         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38967     },
38968     
38969     onRender : function(ctr,pos) {
38970         
38971         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38972         if(!this.config.split){
38973             return;
38974         }
38975         if(!this.split){
38976             
38977             var splitEl = Roo.DomHelper.append(ctr.dom,  {
38978                             tag: "div",
38979                             id: this.el.id + "-split",
38980                             cls: "roo-layout-split roo-layout-split-"+this.position,
38981                             html: "&#160;"
38982             });
38983             /** The SplitBar for this region 
38984             * @type Roo.SplitBar */
38985             // does not exist yet...
38986             Roo.log([this.position, this.orientation]);
38987             
38988             this.split = new Roo.bootstrap.SplitBar({
38989                 dragElement : splitEl,
38990                 resizingElement: this.el,
38991                 orientation : this.orientation
38992             });
38993             
38994             this.split.on("moved", this.onSplitMove, this);
38995             this.split.useShim = this.config.useShim === true;
38996             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38997             if(this.useSplitTips){
38998                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38999             }
39000             //if(config.collapsible){
39001             //    this.split.el.on("dblclick", this.collapse,  this);
39002             //}
39003         }
39004         if(typeof this.config.minSize != "undefined"){
39005             this.split.minSize = this.config.minSize;
39006         }
39007         if(typeof this.config.maxSize != "undefined"){
39008             this.split.maxSize = this.config.maxSize;
39009         }
39010         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39011             this.hideSplitter();
39012         }
39013         
39014     },
39015
39016     getHMaxSize : function(){
39017          var cmax = this.config.maxSize || 10000;
39018          var center = this.mgr.getRegion("center");
39019          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39020     },
39021
39022     getVMaxSize : function(){
39023          var cmax = this.config.maxSize || 10000;
39024          var center = this.mgr.getRegion("center");
39025          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39026     },
39027
39028     onSplitMove : function(split, newSize){
39029         this.fireEvent("resized", this, newSize);
39030     },
39031     
39032     /** 
39033      * Returns the {@link Roo.SplitBar} for this region.
39034      * @return {Roo.SplitBar}
39035      */
39036     getSplitBar : function(){
39037         return this.split;
39038     },
39039     
39040     hide : function(){
39041         this.hideSplitter();
39042         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39043     },
39044
39045     hideSplitter : function(){
39046         if(this.split){
39047             this.split.el.setLocation(-2000,-2000);
39048             this.split.el.hide();
39049         }
39050     },
39051
39052     show : function(){
39053         if(this.split){
39054             this.split.el.show();
39055         }
39056         Roo.bootstrap.layout.Split.superclass.show.call(this);
39057     },
39058     
39059     beforeSlide: function(){
39060         if(Roo.isGecko){// firefox overflow auto bug workaround
39061             this.bodyEl.clip();
39062             if(this.tabs) {
39063                 this.tabs.bodyEl.clip();
39064             }
39065             if(this.activePanel){
39066                 this.activePanel.getEl().clip();
39067                 
39068                 if(this.activePanel.beforeSlide){
39069                     this.activePanel.beforeSlide();
39070                 }
39071             }
39072         }
39073     },
39074     
39075     afterSlide : function(){
39076         if(Roo.isGecko){// firefox overflow auto bug workaround
39077             this.bodyEl.unclip();
39078             if(this.tabs) {
39079                 this.tabs.bodyEl.unclip();
39080             }
39081             if(this.activePanel){
39082                 this.activePanel.getEl().unclip();
39083                 if(this.activePanel.afterSlide){
39084                     this.activePanel.afterSlide();
39085                 }
39086             }
39087         }
39088     },
39089
39090     initAutoHide : function(){
39091         if(this.autoHide !== false){
39092             if(!this.autoHideHd){
39093                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39094                 this.autoHideHd = {
39095                     "mouseout": function(e){
39096                         if(!e.within(this.el, true)){
39097                             st.delay(500);
39098                         }
39099                     },
39100                     "mouseover" : function(e){
39101                         st.cancel();
39102                     },
39103                     scope : this
39104                 };
39105             }
39106             this.el.on(this.autoHideHd);
39107         }
39108     },
39109
39110     clearAutoHide : function(){
39111         if(this.autoHide !== false){
39112             this.el.un("mouseout", this.autoHideHd.mouseout);
39113             this.el.un("mouseover", this.autoHideHd.mouseover);
39114         }
39115     },
39116
39117     clearMonitor : function(){
39118         Roo.get(document).un("click", this.slideInIf, this);
39119     },
39120
39121     // these names are backwards but not changed for compat
39122     slideOut : function(){
39123         if(this.isSlid || this.el.hasActiveFx()){
39124             return;
39125         }
39126         this.isSlid = true;
39127         if(this.collapseBtn){
39128             this.collapseBtn.hide();
39129         }
39130         this.closeBtnState = this.closeBtn.getStyle('display');
39131         this.closeBtn.hide();
39132         if(this.stickBtn){
39133             this.stickBtn.show();
39134         }
39135         this.el.show();
39136         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39137         this.beforeSlide();
39138         this.el.setStyle("z-index", 10001);
39139         this.el.slideIn(this.getSlideAnchor(), {
39140             callback: function(){
39141                 this.afterSlide();
39142                 this.initAutoHide();
39143                 Roo.get(document).on("click", this.slideInIf, this);
39144                 this.fireEvent("slideshow", this);
39145             },
39146             scope: this,
39147             block: true
39148         });
39149     },
39150
39151     afterSlideIn : function(){
39152         this.clearAutoHide();
39153         this.isSlid = false;
39154         this.clearMonitor();
39155         this.el.setStyle("z-index", "");
39156         if(this.collapseBtn){
39157             this.collapseBtn.show();
39158         }
39159         this.closeBtn.setStyle('display', this.closeBtnState);
39160         if(this.stickBtn){
39161             this.stickBtn.hide();
39162         }
39163         this.fireEvent("slidehide", this);
39164     },
39165
39166     slideIn : function(cb){
39167         if(!this.isSlid || this.el.hasActiveFx()){
39168             Roo.callback(cb);
39169             return;
39170         }
39171         this.isSlid = false;
39172         this.beforeSlide();
39173         this.el.slideOut(this.getSlideAnchor(), {
39174             callback: function(){
39175                 this.el.setLeftTop(-10000, -10000);
39176                 this.afterSlide();
39177                 this.afterSlideIn();
39178                 Roo.callback(cb);
39179             },
39180             scope: this,
39181             block: true
39182         });
39183     },
39184     
39185     slideInIf : function(e){
39186         if(!e.within(this.el)){
39187             this.slideIn();
39188         }
39189     },
39190
39191     animateCollapse : function(){
39192         this.beforeSlide();
39193         this.el.setStyle("z-index", 20000);
39194         var anchor = this.getSlideAnchor();
39195         this.el.slideOut(anchor, {
39196             callback : function(){
39197                 this.el.setStyle("z-index", "");
39198                 this.collapsedEl.slideIn(anchor, {duration:.3});
39199                 this.afterSlide();
39200                 this.el.setLocation(-10000,-10000);
39201                 this.el.hide();
39202                 this.fireEvent("collapsed", this);
39203             },
39204             scope: this,
39205             block: true
39206         });
39207     },
39208
39209     animateExpand : function(){
39210         this.beforeSlide();
39211         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39212         this.el.setStyle("z-index", 20000);
39213         this.collapsedEl.hide({
39214             duration:.1
39215         });
39216         this.el.slideIn(this.getSlideAnchor(), {
39217             callback : function(){
39218                 this.el.setStyle("z-index", "");
39219                 this.afterSlide();
39220                 if(this.split){
39221                     this.split.el.show();
39222                 }
39223                 this.fireEvent("invalidated", this);
39224                 this.fireEvent("expanded", this);
39225             },
39226             scope: this,
39227             block: true
39228         });
39229     },
39230
39231     anchors : {
39232         "west" : "left",
39233         "east" : "right",
39234         "north" : "top",
39235         "south" : "bottom"
39236     },
39237
39238     sanchors : {
39239         "west" : "l",
39240         "east" : "r",
39241         "north" : "t",
39242         "south" : "b"
39243     },
39244
39245     canchors : {
39246         "west" : "tl-tr",
39247         "east" : "tr-tl",
39248         "north" : "tl-bl",
39249         "south" : "bl-tl"
39250     },
39251
39252     getAnchor : function(){
39253         return this.anchors[this.position];
39254     },
39255
39256     getCollapseAnchor : function(){
39257         return this.canchors[this.position];
39258     },
39259
39260     getSlideAnchor : function(){
39261         return this.sanchors[this.position];
39262     },
39263
39264     getAlignAdj : function(){
39265         var cm = this.cmargins;
39266         switch(this.position){
39267             case "west":
39268                 return [0, 0];
39269             break;
39270             case "east":
39271                 return [0, 0];
39272             break;
39273             case "north":
39274                 return [0, 0];
39275             break;
39276             case "south":
39277                 return [0, 0];
39278             break;
39279         }
39280     },
39281
39282     getExpandAdj : function(){
39283         var c = this.collapsedEl, cm = this.cmargins;
39284         switch(this.position){
39285             case "west":
39286                 return [-(cm.right+c.getWidth()+cm.left), 0];
39287             break;
39288             case "east":
39289                 return [cm.right+c.getWidth()+cm.left, 0];
39290             break;
39291             case "north":
39292                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39293             break;
39294             case "south":
39295                 return [0, cm.top+cm.bottom+c.getHeight()];
39296             break;
39297         }
39298     }
39299 });/*
39300  * Based on:
39301  * Ext JS Library 1.1.1
39302  * Copyright(c) 2006-2007, Ext JS, LLC.
39303  *
39304  * Originally Released Under LGPL - original licence link has changed is not relivant.
39305  *
39306  * Fork - LGPL
39307  * <script type="text/javascript">
39308  */
39309 /*
39310  * These classes are private internal classes
39311  */
39312 Roo.bootstrap.layout.Center = function(config){
39313     config.region = "center";
39314     Roo.bootstrap.layout.Region.call(this, config);
39315     this.visible = true;
39316     this.minWidth = config.minWidth || 20;
39317     this.minHeight = config.minHeight || 20;
39318 };
39319
39320 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39321     hide : function(){
39322         // center panel can't be hidden
39323     },
39324     
39325     show : function(){
39326         // center panel can't be hidden
39327     },
39328     
39329     getMinWidth: function(){
39330         return this.minWidth;
39331     },
39332     
39333     getMinHeight: function(){
39334         return this.minHeight;
39335     }
39336 });
39337
39338
39339
39340
39341  
39342
39343
39344
39345
39346
39347
39348 Roo.bootstrap.layout.North = function(config)
39349 {
39350     config.region = 'north';
39351     config.cursor = 'n-resize';
39352     
39353     Roo.bootstrap.layout.Split.call(this, config);
39354     
39355     
39356     if(this.split){
39357         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39358         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39359         this.split.el.addClass("roo-layout-split-v");
39360     }
39361     //var size = config.initialSize || config.height;
39362     //if(this.el && typeof size != "undefined"){
39363     //    this.el.setHeight(size);
39364     //}
39365 };
39366 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39367 {
39368     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39369      
39370      
39371     onRender : function(ctr, pos)
39372     {
39373         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39374         var size = this.config.initialSize || this.config.height;
39375         if(this.el && typeof size != "undefined"){
39376             this.el.setHeight(size);
39377         }
39378     
39379     },
39380     
39381     getBox : function(){
39382         if(this.collapsed){
39383             return this.collapsedEl.getBox();
39384         }
39385         var box = this.el.getBox();
39386         if(this.split){
39387             box.height += this.split.el.getHeight();
39388         }
39389         return box;
39390     },
39391     
39392     updateBox : function(box){
39393         if(this.split && !this.collapsed){
39394             box.height -= this.split.el.getHeight();
39395             this.split.el.setLeft(box.x);
39396             this.split.el.setTop(box.y+box.height);
39397             this.split.el.setWidth(box.width);
39398         }
39399         if(this.collapsed){
39400             this.updateBody(box.width, null);
39401         }
39402         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39403     }
39404 });
39405
39406
39407
39408
39409
39410 Roo.bootstrap.layout.South = function(config){
39411     config.region = 'south';
39412     config.cursor = 's-resize';
39413     Roo.bootstrap.layout.Split.call(this, config);
39414     if(this.split){
39415         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39416         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39417         this.split.el.addClass("roo-layout-split-v");
39418     }
39419     
39420 };
39421
39422 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39423     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39424     
39425     onRender : function(ctr, pos)
39426     {
39427         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39428         var size = this.config.initialSize || this.config.height;
39429         if(this.el && typeof size != "undefined"){
39430             this.el.setHeight(size);
39431         }
39432     
39433     },
39434     
39435     getBox : function(){
39436         if(this.collapsed){
39437             return this.collapsedEl.getBox();
39438         }
39439         var box = this.el.getBox();
39440         if(this.split){
39441             var sh = this.split.el.getHeight();
39442             box.height += sh;
39443             box.y -= sh;
39444         }
39445         return box;
39446     },
39447     
39448     updateBox : function(box){
39449         if(this.split && !this.collapsed){
39450             var sh = this.split.el.getHeight();
39451             box.height -= sh;
39452             box.y += sh;
39453             this.split.el.setLeft(box.x);
39454             this.split.el.setTop(box.y-sh);
39455             this.split.el.setWidth(box.width);
39456         }
39457         if(this.collapsed){
39458             this.updateBody(box.width, null);
39459         }
39460         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39461     }
39462 });
39463
39464 Roo.bootstrap.layout.East = function(config){
39465     config.region = "east";
39466     config.cursor = "e-resize";
39467     Roo.bootstrap.layout.Split.call(this, config);
39468     if(this.split){
39469         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39470         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39471         this.split.el.addClass("roo-layout-split-h");
39472     }
39473     
39474 };
39475 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39476     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39477     
39478     onRender : function(ctr, pos)
39479     {
39480         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39481         var size = this.config.initialSize || this.config.width;
39482         if(this.el && typeof size != "undefined"){
39483             this.el.setWidth(size);
39484         }
39485     
39486     },
39487     
39488     getBox : function(){
39489         if(this.collapsed){
39490             return this.collapsedEl.getBox();
39491         }
39492         var box = this.el.getBox();
39493         if(this.split){
39494             var sw = this.split.el.getWidth();
39495             box.width += sw;
39496             box.x -= sw;
39497         }
39498         return box;
39499     },
39500
39501     updateBox : function(box){
39502         if(this.split && !this.collapsed){
39503             var sw = this.split.el.getWidth();
39504             box.width -= sw;
39505             this.split.el.setLeft(box.x);
39506             this.split.el.setTop(box.y);
39507             this.split.el.setHeight(box.height);
39508             box.x += sw;
39509         }
39510         if(this.collapsed){
39511             this.updateBody(null, box.height);
39512         }
39513         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39514     }
39515 });
39516
39517 Roo.bootstrap.layout.West = function(config){
39518     config.region = "west";
39519     config.cursor = "w-resize";
39520     
39521     Roo.bootstrap.layout.Split.call(this, config);
39522     if(this.split){
39523         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39524         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39525         this.split.el.addClass("roo-layout-split-h");
39526     }
39527     
39528 };
39529 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39530     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39531     
39532     onRender: function(ctr, pos)
39533     {
39534         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39535         var size = this.config.initialSize || this.config.width;
39536         if(typeof size != "undefined"){
39537             this.el.setWidth(size);
39538         }
39539     },
39540     
39541     getBox : function(){
39542         if(this.collapsed){
39543             return this.collapsedEl.getBox();
39544         }
39545         var box = this.el.getBox();
39546         if (box.width == 0) {
39547             box.width = this.config.width; // kludge?
39548         }
39549         if(this.split){
39550             box.width += this.split.el.getWidth();
39551         }
39552         return box;
39553     },
39554     
39555     updateBox : function(box){
39556         if(this.split && !this.collapsed){
39557             var sw = this.split.el.getWidth();
39558             box.width -= sw;
39559             this.split.el.setLeft(box.x+box.width);
39560             this.split.el.setTop(box.y);
39561             this.split.el.setHeight(box.height);
39562         }
39563         if(this.collapsed){
39564             this.updateBody(null, box.height);
39565         }
39566         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39567     }
39568 });Roo.namespace("Roo.bootstrap.panel");/*
39569  * Based on:
39570  * Ext JS Library 1.1.1
39571  * Copyright(c) 2006-2007, Ext JS, LLC.
39572  *
39573  * Originally Released Under LGPL - original licence link has changed is not relivant.
39574  *
39575  * Fork - LGPL
39576  * <script type="text/javascript">
39577  */
39578 /**
39579  * @class Roo.ContentPanel
39580  * @extends Roo.util.Observable
39581  * A basic ContentPanel element.
39582  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39583  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39584  * @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
39585  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39586  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39587  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39588  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39589  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39590  * @cfg {String} title          The title for this panel
39591  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39592  * @cfg {String} url            Calls {@link #setUrl} with this value
39593  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39594  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39595  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39596  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39597  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
39598  * @cfg {Boolean} badges render the badges
39599  * @cfg {String} cls  extra classes to use  
39600  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39601
39602  * @constructor
39603  * Create a new ContentPanel.
39604  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39605  * @param {String/Object} config A string to set only the title or a config object
39606  * @param {String} content (optional) Set the HTML content for this panel
39607  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39608  */
39609 Roo.bootstrap.panel.Content = function( config){
39610     
39611     this.tpl = config.tpl || false;
39612     
39613     var el = config.el;
39614     var content = config.content;
39615
39616     if(config.autoCreate){ // xtype is available if this is called from factory
39617         el = Roo.id();
39618     }
39619     this.el = Roo.get(el);
39620     if(!this.el && config && config.autoCreate){
39621         if(typeof config.autoCreate == "object"){
39622             if(!config.autoCreate.id){
39623                 config.autoCreate.id = config.id||el;
39624             }
39625             this.el = Roo.DomHelper.append(document.body,
39626                         config.autoCreate, true);
39627         }else{
39628             var elcfg =  {
39629                 tag: "div",
39630                 cls: (config.cls || '') +
39631                     (config.background ? ' bg-' + config.background : '') +
39632                     " roo-layout-inactive-content",
39633                 id: config.id||el
39634             };
39635             if (config.iframe) {
39636                 elcfg.cn = [
39637                     {
39638                         tag : 'iframe',
39639                         style : 'border: 0px',
39640                         src : 'about:blank'
39641                     }
39642                 ];
39643             }
39644               
39645             if (config.html) {
39646                 elcfg.html = config.html;
39647                 
39648             }
39649                         
39650             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39651             if (config.iframe) {
39652                 this.iframeEl = this.el.select('iframe',true).first();
39653             }
39654             
39655         }
39656     } 
39657     this.closable = false;
39658     this.loaded = false;
39659     this.active = false;
39660    
39661       
39662     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39663         
39664         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39665         
39666         this.wrapEl = this.el; //this.el.wrap();
39667         var ti = [];
39668         if (config.toolbar.items) {
39669             ti = config.toolbar.items ;
39670             delete config.toolbar.items ;
39671         }
39672         
39673         var nitems = [];
39674         this.toolbar.render(this.wrapEl, 'before');
39675         for(var i =0;i < ti.length;i++) {
39676           //  Roo.log(['add child', items[i]]);
39677             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39678         }
39679         this.toolbar.items = nitems;
39680         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39681         delete config.toolbar;
39682         
39683     }
39684     /*
39685     // xtype created footer. - not sure if will work as we normally have to render first..
39686     if (this.footer && !this.footer.el && this.footer.xtype) {
39687         if (!this.wrapEl) {
39688             this.wrapEl = this.el.wrap();
39689         }
39690     
39691         this.footer.container = this.wrapEl.createChild();
39692          
39693         this.footer = Roo.factory(this.footer, Roo);
39694         
39695     }
39696     */
39697     
39698      if(typeof config == "string"){
39699         this.title = config;
39700     }else{
39701         Roo.apply(this, config);
39702     }
39703     
39704     if(this.resizeEl){
39705         this.resizeEl = Roo.get(this.resizeEl, true);
39706     }else{
39707         this.resizeEl = this.el;
39708     }
39709     // handle view.xtype
39710     
39711  
39712     
39713     
39714     this.addEvents({
39715         /**
39716          * @event activate
39717          * Fires when this panel is activated. 
39718          * @param {Roo.ContentPanel} this
39719          */
39720         "activate" : true,
39721         /**
39722          * @event deactivate
39723          * Fires when this panel is activated. 
39724          * @param {Roo.ContentPanel} this
39725          */
39726         "deactivate" : true,
39727
39728         /**
39729          * @event resize
39730          * Fires when this panel is resized if fitToFrame is true.
39731          * @param {Roo.ContentPanel} this
39732          * @param {Number} width The width after any component adjustments
39733          * @param {Number} height The height after any component adjustments
39734          */
39735         "resize" : true,
39736         
39737          /**
39738          * @event render
39739          * Fires when this tab is created
39740          * @param {Roo.ContentPanel} this
39741          */
39742         "render" : true
39743         
39744         
39745         
39746     });
39747     
39748
39749     
39750     
39751     if(this.autoScroll && !this.iframe){
39752         this.resizeEl.setStyle("overflow", "auto");
39753     } else {
39754         // fix randome scrolling
39755         //this.el.on('scroll', function() {
39756         //    Roo.log('fix random scolling');
39757         //    this.scrollTo('top',0); 
39758         //});
39759     }
39760     content = content || this.content;
39761     if(content){
39762         this.setContent(content);
39763     }
39764     if(config && config.url){
39765         this.setUrl(this.url, this.params, this.loadOnce);
39766     }
39767     
39768     
39769     
39770     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39771     
39772     if (this.view && typeof(this.view.xtype) != 'undefined') {
39773         this.view.el = this.el.appendChild(document.createElement("div"));
39774         this.view = Roo.factory(this.view); 
39775         this.view.render  &&  this.view.render(false, '');  
39776     }
39777     
39778     
39779     this.fireEvent('render', this);
39780 };
39781
39782 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39783     
39784     cls : '',
39785     background : '',
39786     
39787     tabTip : '',
39788     
39789     iframe : false,
39790     iframeEl : false,
39791     
39792     setRegion : function(region){
39793         this.region = region;
39794         this.setActiveClass(region && !this.background);
39795     },
39796     
39797     
39798     setActiveClass: function(state)
39799     {
39800         if(state){
39801            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39802            this.el.setStyle('position','relative');
39803         }else{
39804            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39805            this.el.setStyle('position', 'absolute');
39806         } 
39807     },
39808     
39809     /**
39810      * Returns the toolbar for this Panel if one was configured. 
39811      * @return {Roo.Toolbar} 
39812      */
39813     getToolbar : function(){
39814         return this.toolbar;
39815     },
39816     
39817     setActiveState : function(active)
39818     {
39819         this.active = active;
39820         this.setActiveClass(active);
39821         if(!active){
39822             if(this.fireEvent("deactivate", this) === false){
39823                 return false;
39824             }
39825             return true;
39826         }
39827         this.fireEvent("activate", this);
39828         return true;
39829     },
39830     /**
39831      * Updates this panel's element (not for iframe)
39832      * @param {String} content The new content
39833      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39834     */
39835     setContent : function(content, loadScripts){
39836         if (this.iframe) {
39837             return;
39838         }
39839         
39840         this.el.update(content, loadScripts);
39841     },
39842
39843     ignoreResize : function(w, h){
39844         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39845             return true;
39846         }else{
39847             this.lastSize = {width: w, height: h};
39848             return false;
39849         }
39850     },
39851     /**
39852      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39853      * @return {Roo.UpdateManager} The UpdateManager
39854      */
39855     getUpdateManager : function(){
39856         if (this.iframe) {
39857             return false;
39858         }
39859         return this.el.getUpdateManager();
39860     },
39861      /**
39862      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39863      * Does not work with IFRAME contents
39864      * @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:
39865 <pre><code>
39866 panel.load({
39867     url: "your-url.php",
39868     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39869     callback: yourFunction,
39870     scope: yourObject, //(optional scope)
39871     discardUrl: false,
39872     nocache: false,
39873     text: "Loading...",
39874     timeout: 30,
39875     scripts: false
39876 });
39877 </code></pre>
39878      
39879      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39880      * 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.
39881      * @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}
39882      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39883      * @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.
39884      * @return {Roo.ContentPanel} this
39885      */
39886     load : function(){
39887         
39888         if (this.iframe) {
39889             return this;
39890         }
39891         
39892         var um = this.el.getUpdateManager();
39893         um.update.apply(um, arguments);
39894         return this;
39895     },
39896
39897
39898     /**
39899      * 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.
39900      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39901      * @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)
39902      * @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)
39903      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
39904      */
39905     setUrl : function(url, params, loadOnce){
39906         if (this.iframe) {
39907             this.iframeEl.dom.src = url;
39908             return false;
39909         }
39910         
39911         if(this.refreshDelegate){
39912             this.removeListener("activate", this.refreshDelegate);
39913         }
39914         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39915         this.on("activate", this.refreshDelegate);
39916         return this.el.getUpdateManager();
39917     },
39918     
39919     _handleRefresh : function(url, params, loadOnce){
39920         if(!loadOnce || !this.loaded){
39921             var updater = this.el.getUpdateManager();
39922             updater.update(url, params, this._setLoaded.createDelegate(this));
39923         }
39924     },
39925     
39926     _setLoaded : function(){
39927         this.loaded = true;
39928     }, 
39929     
39930     /**
39931      * Returns this panel's id
39932      * @return {String} 
39933      */
39934     getId : function(){
39935         return this.el.id;
39936     },
39937     
39938     /** 
39939      * Returns this panel's element - used by regiosn to add.
39940      * @return {Roo.Element} 
39941      */
39942     getEl : function(){
39943         return this.wrapEl || this.el;
39944     },
39945     
39946    
39947     
39948     adjustForComponents : function(width, height)
39949     {
39950         //Roo.log('adjustForComponents ');
39951         if(this.resizeEl != this.el){
39952             width -= this.el.getFrameWidth('lr');
39953             height -= this.el.getFrameWidth('tb');
39954         }
39955         if(this.toolbar){
39956             var te = this.toolbar.getEl();
39957             te.setWidth(width);
39958             height -= te.getHeight();
39959         }
39960         if(this.footer){
39961             var te = this.footer.getEl();
39962             te.setWidth(width);
39963             height -= te.getHeight();
39964         }
39965         
39966         
39967         if(this.adjustments){
39968             width += this.adjustments[0];
39969             height += this.adjustments[1];
39970         }
39971         return {"width": width, "height": height};
39972     },
39973     
39974     setSize : function(width, height){
39975         if(this.fitToFrame && !this.ignoreResize(width, height)){
39976             if(this.fitContainer && this.resizeEl != this.el){
39977                 this.el.setSize(width, height);
39978             }
39979             var size = this.adjustForComponents(width, height);
39980             if (this.iframe) {
39981                 this.iframeEl.setSize(width,height);
39982             }
39983             
39984             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39985             this.fireEvent('resize', this, size.width, size.height);
39986             
39987             
39988         }
39989     },
39990     
39991     /**
39992      * Returns this panel's title
39993      * @return {String} 
39994      */
39995     getTitle : function(){
39996         
39997         if (typeof(this.title) != 'object') {
39998             return this.title;
39999         }
40000         
40001         var t = '';
40002         for (var k in this.title) {
40003             if (!this.title.hasOwnProperty(k)) {
40004                 continue;
40005             }
40006             
40007             if (k.indexOf('-') >= 0) {
40008                 var s = k.split('-');
40009                 for (var i = 0; i<s.length; i++) {
40010                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40011                 }
40012             } else {
40013                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40014             }
40015         }
40016         return t;
40017     },
40018     
40019     /**
40020      * Set this panel's title
40021      * @param {String} title
40022      */
40023     setTitle : function(title){
40024         this.title = title;
40025         if(this.region){
40026             this.region.updatePanelTitle(this, title);
40027         }
40028     },
40029     
40030     /**
40031      * Returns true is this panel was configured to be closable
40032      * @return {Boolean} 
40033      */
40034     isClosable : function(){
40035         return this.closable;
40036     },
40037     
40038     beforeSlide : function(){
40039         this.el.clip();
40040         this.resizeEl.clip();
40041     },
40042     
40043     afterSlide : function(){
40044         this.el.unclip();
40045         this.resizeEl.unclip();
40046     },
40047     
40048     /**
40049      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40050      *   Will fail silently if the {@link #setUrl} method has not been called.
40051      *   This does not activate the panel, just updates its content.
40052      */
40053     refresh : function(){
40054         if(this.refreshDelegate){
40055            this.loaded = false;
40056            this.refreshDelegate();
40057         }
40058     },
40059     
40060     /**
40061      * Destroys this panel
40062      */
40063     destroy : function(){
40064         this.el.removeAllListeners();
40065         var tempEl = document.createElement("span");
40066         tempEl.appendChild(this.el.dom);
40067         tempEl.innerHTML = "";
40068         this.el.remove();
40069         this.el = null;
40070     },
40071     
40072     /**
40073      * form - if the content panel contains a form - this is a reference to it.
40074      * @type {Roo.form.Form}
40075      */
40076     form : false,
40077     /**
40078      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40079      *    This contains a reference to it.
40080      * @type {Roo.View}
40081      */
40082     view : false,
40083     
40084       /**
40085      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40086      * <pre><code>
40087
40088 layout.addxtype({
40089        xtype : 'Form',
40090        items: [ .... ]
40091    }
40092 );
40093
40094 </code></pre>
40095      * @param {Object} cfg Xtype definition of item to add.
40096      */
40097     
40098     
40099     getChildContainer: function () {
40100         return this.getEl();
40101     }
40102     
40103     
40104     /*
40105         var  ret = new Roo.factory(cfg);
40106         return ret;
40107         
40108         
40109         // add form..
40110         if (cfg.xtype.match(/^Form$/)) {
40111             
40112             var el;
40113             //if (this.footer) {
40114             //    el = this.footer.container.insertSibling(false, 'before');
40115             //} else {
40116                 el = this.el.createChild();
40117             //}
40118
40119             this.form = new  Roo.form.Form(cfg);
40120             
40121             
40122             if ( this.form.allItems.length) {
40123                 this.form.render(el.dom);
40124             }
40125             return this.form;
40126         }
40127         // should only have one of theses..
40128         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40129             // views.. should not be just added - used named prop 'view''
40130             
40131             cfg.el = this.el.appendChild(document.createElement("div"));
40132             // factory?
40133             
40134             var ret = new Roo.factory(cfg);
40135              
40136              ret.render && ret.render(false, ''); // render blank..
40137             this.view = ret;
40138             return ret;
40139         }
40140         return false;
40141     }
40142     \*/
40143 });
40144  
40145 /**
40146  * @class Roo.bootstrap.panel.Grid
40147  * @extends Roo.bootstrap.panel.Content
40148  * @constructor
40149  * Create a new GridPanel.
40150  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40151  * @param {Object} config A the config object
40152   
40153  */
40154
40155
40156
40157 Roo.bootstrap.panel.Grid = function(config)
40158 {
40159     
40160       
40161     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40162         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40163
40164     config.el = this.wrapper;
40165     //this.el = this.wrapper;
40166     
40167       if (config.container) {
40168         // ctor'ed from a Border/panel.grid
40169         
40170         
40171         this.wrapper.setStyle("overflow", "hidden");
40172         this.wrapper.addClass('roo-grid-container');
40173
40174     }
40175     
40176     
40177     if(config.toolbar){
40178         var tool_el = this.wrapper.createChild();    
40179         this.toolbar = Roo.factory(config.toolbar);
40180         var ti = [];
40181         if (config.toolbar.items) {
40182             ti = config.toolbar.items ;
40183             delete config.toolbar.items ;
40184         }
40185         
40186         var nitems = [];
40187         this.toolbar.render(tool_el);
40188         for(var i =0;i < ti.length;i++) {
40189           //  Roo.log(['add child', items[i]]);
40190             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40191         }
40192         this.toolbar.items = nitems;
40193         
40194         delete config.toolbar;
40195     }
40196     
40197     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40198     config.grid.scrollBody = true;;
40199     config.grid.monitorWindowResize = false; // turn off autosizing
40200     config.grid.autoHeight = false;
40201     config.grid.autoWidth = false;
40202     
40203     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40204     
40205     if (config.background) {
40206         // render grid on panel activation (if panel background)
40207         this.on('activate', function(gp) {
40208             if (!gp.grid.rendered) {
40209                 gp.grid.render(this.wrapper);
40210                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40211             }
40212         });
40213             
40214     } else {
40215         this.grid.render(this.wrapper);
40216         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40217
40218     }
40219     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40220     // ??? needed ??? config.el = this.wrapper;
40221     
40222     
40223     
40224   
40225     // xtype created footer. - not sure if will work as we normally have to render first..
40226     if (this.footer && !this.footer.el && this.footer.xtype) {
40227         
40228         var ctr = this.grid.getView().getFooterPanel(true);
40229         this.footer.dataSource = this.grid.dataSource;
40230         this.footer = Roo.factory(this.footer, Roo);
40231         this.footer.render(ctr);
40232         
40233     }
40234     
40235     
40236     
40237     
40238      
40239 };
40240
40241 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40242     getId : function(){
40243         return this.grid.id;
40244     },
40245     
40246     /**
40247      * Returns the grid for this panel
40248      * @return {Roo.bootstrap.Table} 
40249      */
40250     getGrid : function(){
40251         return this.grid;    
40252     },
40253     
40254     setSize : function(width, height){
40255         if(!this.ignoreResize(width, height)){
40256             var grid = this.grid;
40257             var size = this.adjustForComponents(width, height);
40258             // tfoot is not a footer?
40259           
40260             
40261             var gridel = grid.getGridEl();
40262             gridel.setSize(size.width, size.height);
40263             
40264             var tbd = grid.getGridEl().select('tbody', true).first();
40265             var thd = grid.getGridEl().select('thead',true).first();
40266             var tbf= grid.getGridEl().select('tfoot', true).first();
40267
40268             if (tbf) {
40269                 size.height -= tbf.getHeight();
40270             }
40271             if (thd) {
40272                 size.height -= thd.getHeight();
40273             }
40274             
40275             tbd.setSize(size.width, size.height );
40276             // this is for the account management tab -seems to work there.
40277             var thd = grid.getGridEl().select('thead',true).first();
40278             //if (tbd) {
40279             //    tbd.setSize(size.width, size.height - thd.getHeight());
40280             //}
40281              
40282             grid.autoSize();
40283         }
40284     },
40285      
40286     
40287     
40288     beforeSlide : function(){
40289         this.grid.getView().scroller.clip();
40290     },
40291     
40292     afterSlide : function(){
40293         this.grid.getView().scroller.unclip();
40294     },
40295     
40296     destroy : function(){
40297         this.grid.destroy();
40298         delete this.grid;
40299         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40300     }
40301 });
40302
40303 /**
40304  * @class Roo.bootstrap.panel.Nest
40305  * @extends Roo.bootstrap.panel.Content
40306  * @constructor
40307  * Create a new Panel, that can contain a layout.Border.
40308  * 
40309  * 
40310  * @param {Roo.BorderLayout} layout The layout for this panel
40311  * @param {String/Object} config A string to set only the title or a config object
40312  */
40313 Roo.bootstrap.panel.Nest = function(config)
40314 {
40315     // construct with only one argument..
40316     /* FIXME - implement nicer consturctors
40317     if (layout.layout) {
40318         config = layout;
40319         layout = config.layout;
40320         delete config.layout;
40321     }
40322     if (layout.xtype && !layout.getEl) {
40323         // then layout needs constructing..
40324         layout = Roo.factory(layout, Roo);
40325     }
40326     */
40327     
40328     config.el =  config.layout.getEl();
40329     
40330     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40331     
40332     config.layout.monitorWindowResize = false; // turn off autosizing
40333     this.layout = config.layout;
40334     this.layout.getEl().addClass("roo-layout-nested-layout");
40335     this.layout.parent = this;
40336     
40337     
40338     
40339     
40340 };
40341
40342 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40343
40344     setSize : function(width, height){
40345         if(!this.ignoreResize(width, height)){
40346             var size = this.adjustForComponents(width, height);
40347             var el = this.layout.getEl();
40348             if (size.height < 1) {
40349                 el.setWidth(size.width);   
40350             } else {
40351                 el.setSize(size.width, size.height);
40352             }
40353             var touch = el.dom.offsetWidth;
40354             this.layout.layout();
40355             // ie requires a double layout on the first pass
40356             if(Roo.isIE && !this.initialized){
40357                 this.initialized = true;
40358                 this.layout.layout();
40359             }
40360         }
40361     },
40362     
40363     // activate all subpanels if not currently active..
40364     
40365     setActiveState : function(active){
40366         this.active = active;
40367         this.setActiveClass(active);
40368         
40369         if(!active){
40370             this.fireEvent("deactivate", this);
40371             return;
40372         }
40373         
40374         this.fireEvent("activate", this);
40375         // not sure if this should happen before or after..
40376         if (!this.layout) {
40377             return; // should not happen..
40378         }
40379         var reg = false;
40380         for (var r in this.layout.regions) {
40381             reg = this.layout.getRegion(r);
40382             if (reg.getActivePanel()) {
40383                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40384                 reg.setActivePanel(reg.getActivePanel());
40385                 continue;
40386             }
40387             if (!reg.panels.length) {
40388                 continue;
40389             }
40390             reg.showPanel(reg.getPanel(0));
40391         }
40392         
40393         
40394         
40395         
40396     },
40397     
40398     /**
40399      * Returns the nested BorderLayout for this panel
40400      * @return {Roo.BorderLayout} 
40401      */
40402     getLayout : function(){
40403         return this.layout;
40404     },
40405     
40406      /**
40407      * Adds a xtype elements to the layout of the nested panel
40408      * <pre><code>
40409
40410 panel.addxtype({
40411        xtype : 'ContentPanel',
40412        region: 'west',
40413        items: [ .... ]
40414    }
40415 );
40416
40417 panel.addxtype({
40418         xtype : 'NestedLayoutPanel',
40419         region: 'west',
40420         layout: {
40421            center: { },
40422            west: { }   
40423         },
40424         items : [ ... list of content panels or nested layout panels.. ]
40425    }
40426 );
40427 </code></pre>
40428      * @param {Object} cfg Xtype definition of item to add.
40429      */
40430     addxtype : function(cfg) {
40431         return this.layout.addxtype(cfg);
40432     
40433     }
40434 });/*
40435  * Based on:
40436  * Ext JS Library 1.1.1
40437  * Copyright(c) 2006-2007, Ext JS, LLC.
40438  *
40439  * Originally Released Under LGPL - original licence link has changed is not relivant.
40440  *
40441  * Fork - LGPL
40442  * <script type="text/javascript">
40443  */
40444 /**
40445  * @class Roo.TabPanel
40446  * @extends Roo.util.Observable
40447  * A lightweight tab container.
40448  * <br><br>
40449  * Usage:
40450  * <pre><code>
40451 // basic tabs 1, built from existing content
40452 var tabs = new Roo.TabPanel("tabs1");
40453 tabs.addTab("script", "View Script");
40454 tabs.addTab("markup", "View Markup");
40455 tabs.activate("script");
40456
40457 // more advanced tabs, built from javascript
40458 var jtabs = new Roo.TabPanel("jtabs");
40459 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40460
40461 // set up the UpdateManager
40462 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40463 var updater = tab2.getUpdateManager();
40464 updater.setDefaultUrl("ajax1.htm");
40465 tab2.on('activate', updater.refresh, updater, true);
40466
40467 // Use setUrl for Ajax loading
40468 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40469 tab3.setUrl("ajax2.htm", null, true);
40470
40471 // Disabled tab
40472 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40473 tab4.disable();
40474
40475 jtabs.activate("jtabs-1");
40476  * </code></pre>
40477  * @constructor
40478  * Create a new TabPanel.
40479  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40480  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40481  */
40482 Roo.bootstrap.panel.Tabs = function(config){
40483     /**
40484     * The container element for this TabPanel.
40485     * @type Roo.Element
40486     */
40487     this.el = Roo.get(config.el);
40488     delete config.el;
40489     if(config){
40490         if(typeof config == "boolean"){
40491             this.tabPosition = config ? "bottom" : "top";
40492         }else{
40493             Roo.apply(this, config);
40494         }
40495     }
40496     
40497     if(this.tabPosition == "bottom"){
40498         // if tabs are at the bottom = create the body first.
40499         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40500         this.el.addClass("roo-tabs-bottom");
40501     }
40502     // next create the tabs holders
40503     
40504     if (this.tabPosition == "west"){
40505         
40506         var reg = this.region; // fake it..
40507         while (reg) {
40508             if (!reg.mgr.parent) {
40509                 break;
40510             }
40511             reg = reg.mgr.parent.region;
40512         }
40513         Roo.log("got nest?");
40514         Roo.log(reg);
40515         if (reg.mgr.getRegion('west')) {
40516             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40517             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40518             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40519             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40520             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40521         
40522             
40523         }
40524         
40525         
40526     } else {
40527      
40528         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40529         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40530         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40531         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40532     }
40533     
40534     
40535     if(Roo.isIE){
40536         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40537     }
40538     
40539     // finally - if tabs are at the top, then create the body last..
40540     if(this.tabPosition != "bottom"){
40541         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40542          * @type Roo.Element
40543          */
40544         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40545         this.el.addClass("roo-tabs-top");
40546     }
40547     this.items = [];
40548
40549     this.bodyEl.setStyle("position", "relative");
40550
40551     this.active = null;
40552     this.activateDelegate = this.activate.createDelegate(this);
40553
40554     this.addEvents({
40555         /**
40556          * @event tabchange
40557          * Fires when the active tab changes
40558          * @param {Roo.TabPanel} this
40559          * @param {Roo.TabPanelItem} activePanel The new active tab
40560          */
40561         "tabchange": true,
40562         /**
40563          * @event beforetabchange
40564          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40565          * @param {Roo.TabPanel} this
40566          * @param {Object} e Set cancel to true on this object to cancel the tab change
40567          * @param {Roo.TabPanelItem} tab The tab being changed to
40568          */
40569         "beforetabchange" : true
40570     });
40571
40572     Roo.EventManager.onWindowResize(this.onResize, this);
40573     this.cpad = this.el.getPadding("lr");
40574     this.hiddenCount = 0;
40575
40576
40577     // toolbar on the tabbar support...
40578     if (this.toolbar) {
40579         alert("no toolbar support yet");
40580         this.toolbar  = false;
40581         /*
40582         var tcfg = this.toolbar;
40583         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40584         this.toolbar = new Roo.Toolbar(tcfg);
40585         if (Roo.isSafari) {
40586             var tbl = tcfg.container.child('table', true);
40587             tbl.setAttribute('width', '100%');
40588         }
40589         */
40590         
40591     }
40592    
40593
40594
40595     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40596 };
40597
40598 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40599     /*
40600      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40601      */
40602     tabPosition : "top",
40603     /*
40604      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40605      */
40606     currentTabWidth : 0,
40607     /*
40608      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40609      */
40610     minTabWidth : 40,
40611     /*
40612      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40613      */
40614     maxTabWidth : 250,
40615     /*
40616      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40617      */
40618     preferredTabWidth : 175,
40619     /*
40620      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40621      */
40622     resizeTabs : false,
40623     /*
40624      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40625      */
40626     monitorResize : true,
40627     /*
40628      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40629      */
40630     toolbar : false,  // set by caller..
40631     
40632     region : false, /// set by caller
40633     
40634     disableTooltips : true, // not used yet...
40635
40636     /**
40637      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40638      * @param {String} id The id of the div to use <b>or create</b>
40639      * @param {String} text The text for the tab
40640      * @param {String} content (optional) Content to put in the TabPanelItem body
40641      * @param {Boolean} closable (optional) True to create a close icon on the tab
40642      * @return {Roo.TabPanelItem} The created TabPanelItem
40643      */
40644     addTab : function(id, text, content, closable, tpl)
40645     {
40646         var item = new Roo.bootstrap.panel.TabItem({
40647             panel: this,
40648             id : id,
40649             text : text,
40650             closable : closable,
40651             tpl : tpl
40652         });
40653         this.addTabItem(item);
40654         if(content){
40655             item.setContent(content);
40656         }
40657         return item;
40658     },
40659
40660     /**
40661      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40662      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40663      * @return {Roo.TabPanelItem}
40664      */
40665     getTab : function(id){
40666         return this.items[id];
40667     },
40668
40669     /**
40670      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40671      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40672      */
40673     hideTab : function(id){
40674         var t = this.items[id];
40675         if(!t.isHidden()){
40676            t.setHidden(true);
40677            this.hiddenCount++;
40678            this.autoSizeTabs();
40679         }
40680     },
40681
40682     /**
40683      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40684      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40685      */
40686     unhideTab : function(id){
40687         var t = this.items[id];
40688         if(t.isHidden()){
40689            t.setHidden(false);
40690            this.hiddenCount--;
40691            this.autoSizeTabs();
40692         }
40693     },
40694
40695     /**
40696      * Adds an existing {@link Roo.TabPanelItem}.
40697      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40698      */
40699     addTabItem : function(item)
40700     {
40701         this.items[item.id] = item;
40702         this.items.push(item);
40703         this.autoSizeTabs();
40704       //  if(this.resizeTabs){
40705     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40706   //         this.autoSizeTabs();
40707 //        }else{
40708 //            item.autoSize();
40709        // }
40710     },
40711
40712     /**
40713      * Removes a {@link Roo.TabPanelItem}.
40714      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40715      */
40716     removeTab : function(id){
40717         var items = this.items;
40718         var tab = items[id];
40719         if(!tab) { return; }
40720         var index = items.indexOf(tab);
40721         if(this.active == tab && items.length > 1){
40722             var newTab = this.getNextAvailable(index);
40723             if(newTab) {
40724                 newTab.activate();
40725             }
40726         }
40727         this.stripEl.dom.removeChild(tab.pnode.dom);
40728         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40729             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40730         }
40731         items.splice(index, 1);
40732         delete this.items[tab.id];
40733         tab.fireEvent("close", tab);
40734         tab.purgeListeners();
40735         this.autoSizeTabs();
40736     },
40737
40738     getNextAvailable : function(start){
40739         var items = this.items;
40740         var index = start;
40741         // look for a next tab that will slide over to
40742         // replace the one being removed
40743         while(index < items.length){
40744             var item = items[++index];
40745             if(item && !item.isHidden()){
40746                 return item;
40747             }
40748         }
40749         // if one isn't found select the previous tab (on the left)
40750         index = start;
40751         while(index >= 0){
40752             var item = items[--index];
40753             if(item && !item.isHidden()){
40754                 return item;
40755             }
40756         }
40757         return null;
40758     },
40759
40760     /**
40761      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40762      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40763      */
40764     disableTab : function(id){
40765         var tab = this.items[id];
40766         if(tab && this.active != tab){
40767             tab.disable();
40768         }
40769     },
40770
40771     /**
40772      * Enables a {@link Roo.TabPanelItem} that is disabled.
40773      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40774      */
40775     enableTab : function(id){
40776         var tab = this.items[id];
40777         tab.enable();
40778     },
40779
40780     /**
40781      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40782      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40783      * @return {Roo.TabPanelItem} The TabPanelItem.
40784      */
40785     activate : function(id)
40786     {
40787         //Roo.log('activite:'  + id);
40788         
40789         var tab = this.items[id];
40790         if(!tab){
40791             return null;
40792         }
40793         if(tab == this.active || tab.disabled){
40794             return tab;
40795         }
40796         var e = {};
40797         this.fireEvent("beforetabchange", this, e, tab);
40798         if(e.cancel !== true && !tab.disabled){
40799             if(this.active){
40800                 this.active.hide();
40801             }
40802             this.active = this.items[id];
40803             this.active.show();
40804             this.fireEvent("tabchange", this, this.active);
40805         }
40806         return tab;
40807     },
40808
40809     /**
40810      * Gets the active {@link Roo.TabPanelItem}.
40811      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40812      */
40813     getActiveTab : function(){
40814         return this.active;
40815     },
40816
40817     /**
40818      * Updates the tab body element to fit the height of the container element
40819      * for overflow scrolling
40820      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40821      */
40822     syncHeight : function(targetHeight){
40823         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40824         var bm = this.bodyEl.getMargins();
40825         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40826         this.bodyEl.setHeight(newHeight);
40827         return newHeight;
40828     },
40829
40830     onResize : function(){
40831         if(this.monitorResize){
40832             this.autoSizeTabs();
40833         }
40834     },
40835
40836     /**
40837      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40838      */
40839     beginUpdate : function(){
40840         this.updating = true;
40841     },
40842
40843     /**
40844      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40845      */
40846     endUpdate : function(){
40847         this.updating = false;
40848         this.autoSizeTabs();
40849     },
40850
40851     /**
40852      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40853      */
40854     autoSizeTabs : function()
40855     {
40856         var count = this.items.length;
40857         var vcount = count - this.hiddenCount;
40858         
40859         if (vcount < 2) {
40860             this.stripEl.hide();
40861         } else {
40862             this.stripEl.show();
40863         }
40864         
40865         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40866             return;
40867         }
40868         
40869         
40870         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40871         var availWidth = Math.floor(w / vcount);
40872         var b = this.stripBody;
40873         if(b.getWidth() > w){
40874             var tabs = this.items;
40875             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40876             if(availWidth < this.minTabWidth){
40877                 /*if(!this.sleft){    // incomplete scrolling code
40878                     this.createScrollButtons();
40879                 }
40880                 this.showScroll();
40881                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40882             }
40883         }else{
40884             if(this.currentTabWidth < this.preferredTabWidth){
40885                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40886             }
40887         }
40888     },
40889
40890     /**
40891      * Returns the number of tabs in this TabPanel.
40892      * @return {Number}
40893      */
40894      getCount : function(){
40895          return this.items.length;
40896      },
40897
40898     /**
40899      * Resizes all the tabs to the passed width
40900      * @param {Number} The new width
40901      */
40902     setTabWidth : function(width){
40903         this.currentTabWidth = width;
40904         for(var i = 0, len = this.items.length; i < len; i++) {
40905                 if(!this.items[i].isHidden()) {
40906                 this.items[i].setWidth(width);
40907             }
40908         }
40909     },
40910
40911     /**
40912      * Destroys this TabPanel
40913      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40914      */
40915     destroy : function(removeEl){
40916         Roo.EventManager.removeResizeListener(this.onResize, this);
40917         for(var i = 0, len = this.items.length; i < len; i++){
40918             this.items[i].purgeListeners();
40919         }
40920         if(removeEl === true){
40921             this.el.update("");
40922             this.el.remove();
40923         }
40924     },
40925     
40926     createStrip : function(container)
40927     {
40928         var strip = document.createElement("nav");
40929         strip.className = Roo.bootstrap.version == 4 ?
40930             "navbar-light bg-light" : 
40931             "navbar navbar-default"; //"x-tabs-wrap";
40932         container.appendChild(strip);
40933         return strip;
40934     },
40935     
40936     createStripList : function(strip)
40937     {
40938         // div wrapper for retard IE
40939         // returns the "tr" element.
40940         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40941         //'<div class="x-tabs-strip-wrap">'+
40942           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40943           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40944         return strip.firstChild; //.firstChild.firstChild.firstChild;
40945     },
40946     createBody : function(container)
40947     {
40948         var body = document.createElement("div");
40949         Roo.id(body, "tab-body");
40950         //Roo.fly(body).addClass("x-tabs-body");
40951         Roo.fly(body).addClass("tab-content");
40952         container.appendChild(body);
40953         return body;
40954     },
40955     createItemBody :function(bodyEl, id){
40956         var body = Roo.getDom(id);
40957         if(!body){
40958             body = document.createElement("div");
40959             body.id = id;
40960         }
40961         //Roo.fly(body).addClass("x-tabs-item-body");
40962         Roo.fly(body).addClass("tab-pane");
40963          bodyEl.insertBefore(body, bodyEl.firstChild);
40964         return body;
40965     },
40966     /** @private */
40967     createStripElements :  function(stripEl, text, closable, tpl)
40968     {
40969         var td = document.createElement("li"); // was td..
40970         td.className = 'nav-item';
40971         
40972         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40973         
40974         
40975         stripEl.appendChild(td);
40976         /*if(closable){
40977             td.className = "x-tabs-closable";
40978             if(!this.closeTpl){
40979                 this.closeTpl = new Roo.Template(
40980                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40981                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40982                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
40983                 );
40984             }
40985             var el = this.closeTpl.overwrite(td, {"text": text});
40986             var close = el.getElementsByTagName("div")[0];
40987             var inner = el.getElementsByTagName("em")[0];
40988             return {"el": el, "close": close, "inner": inner};
40989         } else {
40990         */
40991         // not sure what this is..
40992 //            if(!this.tabTpl){
40993                 //this.tabTpl = new Roo.Template(
40994                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40995                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40996                 //);
40997 //                this.tabTpl = new Roo.Template(
40998 //                   '<a href="#">' +
40999 //                   '<span unselectable="on"' +
41000 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41001 //                            ' >{text}</span></a>'
41002 //                );
41003 //                
41004 //            }
41005
41006
41007             var template = tpl || this.tabTpl || false;
41008             
41009             if(!template){
41010                 template =  new Roo.Template(
41011                         Roo.bootstrap.version == 4 ? 
41012                             (
41013                                 '<a class="nav-link" href="#" unselectable="on"' +
41014                                      (this.disableTooltips ? '' : ' title="{text}"') +
41015                                      ' >{text}</a>'
41016                             ) : (
41017                                 '<a class="nav-link" href="#">' +
41018                                 '<span unselectable="on"' +
41019                                          (this.disableTooltips ? '' : ' title="{text}"') +
41020                                     ' >{text}</span></a>'
41021                             )
41022                 );
41023             }
41024             
41025             switch (typeof(template)) {
41026                 case 'object' :
41027                     break;
41028                 case 'string' :
41029                     template = new Roo.Template(template);
41030                     break;
41031                 default :
41032                     break;
41033             }
41034             
41035             var el = template.overwrite(td, {"text": text});
41036             
41037             var inner = el.getElementsByTagName("span")[0];
41038             
41039             return {"el": el, "inner": inner};
41040             
41041     }
41042         
41043     
41044 });
41045
41046 /**
41047  * @class Roo.TabPanelItem
41048  * @extends Roo.util.Observable
41049  * Represents an individual item (tab plus body) in a TabPanel.
41050  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41051  * @param {String} id The id of this TabPanelItem
41052  * @param {String} text The text for the tab of this TabPanelItem
41053  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41054  */
41055 Roo.bootstrap.panel.TabItem = function(config){
41056     /**
41057      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41058      * @type Roo.TabPanel
41059      */
41060     this.tabPanel = config.panel;
41061     /**
41062      * The id for this TabPanelItem
41063      * @type String
41064      */
41065     this.id = config.id;
41066     /** @private */
41067     this.disabled = false;
41068     /** @private */
41069     this.text = config.text;
41070     /** @private */
41071     this.loaded = false;
41072     this.closable = config.closable;
41073
41074     /**
41075      * The body element for this TabPanelItem.
41076      * @type Roo.Element
41077      */
41078     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41079     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41080     this.bodyEl.setStyle("display", "block");
41081     this.bodyEl.setStyle("zoom", "1");
41082     //this.hideAction();
41083
41084     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41085     /** @private */
41086     this.el = Roo.get(els.el);
41087     this.inner = Roo.get(els.inner, true);
41088      this.textEl = Roo.bootstrap.version == 4 ?
41089         this.el : Roo.get(this.el.dom.firstChild, true);
41090
41091     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41092     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41093
41094     
41095 //    this.el.on("mousedown", this.onTabMouseDown, this);
41096     this.el.on("click", this.onTabClick, this);
41097     /** @private */
41098     if(config.closable){
41099         var c = Roo.get(els.close, true);
41100         c.dom.title = this.closeText;
41101         c.addClassOnOver("close-over");
41102         c.on("click", this.closeClick, this);
41103      }
41104
41105     this.addEvents({
41106          /**
41107          * @event activate
41108          * Fires when this tab becomes the active tab.
41109          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41110          * @param {Roo.TabPanelItem} this
41111          */
41112         "activate": true,
41113         /**
41114          * @event beforeclose
41115          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41116          * @param {Roo.TabPanelItem} this
41117          * @param {Object} e Set cancel to true on this object to cancel the close.
41118          */
41119         "beforeclose": true,
41120         /**
41121          * @event close
41122          * Fires when this tab is closed.
41123          * @param {Roo.TabPanelItem} this
41124          */
41125          "close": true,
41126         /**
41127          * @event deactivate
41128          * Fires when this tab is no longer the active tab.
41129          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41130          * @param {Roo.TabPanelItem} this
41131          */
41132          "deactivate" : true
41133     });
41134     this.hidden = false;
41135
41136     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41137 };
41138
41139 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41140            {
41141     purgeListeners : function(){
41142        Roo.util.Observable.prototype.purgeListeners.call(this);
41143        this.el.removeAllListeners();
41144     },
41145     /**
41146      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41147      */
41148     show : function(){
41149         this.status_node.addClass("active");
41150         this.showAction();
41151         if(Roo.isOpera){
41152             this.tabPanel.stripWrap.repaint();
41153         }
41154         this.fireEvent("activate", this.tabPanel, this);
41155     },
41156
41157     /**
41158      * Returns true if this tab is the active tab.
41159      * @return {Boolean}
41160      */
41161     isActive : function(){
41162         return this.tabPanel.getActiveTab() == this;
41163     },
41164
41165     /**
41166      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41167      */
41168     hide : function(){
41169         this.status_node.removeClass("active");
41170         this.hideAction();
41171         this.fireEvent("deactivate", this.tabPanel, this);
41172     },
41173
41174     hideAction : function(){
41175         this.bodyEl.hide();
41176         this.bodyEl.setStyle("position", "absolute");
41177         this.bodyEl.setLeft("-20000px");
41178         this.bodyEl.setTop("-20000px");
41179     },
41180
41181     showAction : function(){
41182         this.bodyEl.setStyle("position", "relative");
41183         this.bodyEl.setTop("");
41184         this.bodyEl.setLeft("");
41185         this.bodyEl.show();
41186     },
41187
41188     /**
41189      * Set the tooltip for the tab.
41190      * @param {String} tooltip The tab's tooltip
41191      */
41192     setTooltip : function(text){
41193         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41194             this.textEl.dom.qtip = text;
41195             this.textEl.dom.removeAttribute('title');
41196         }else{
41197             this.textEl.dom.title = text;
41198         }
41199     },
41200
41201     onTabClick : function(e){
41202         e.preventDefault();
41203         this.tabPanel.activate(this.id);
41204     },
41205
41206     onTabMouseDown : function(e){
41207         e.preventDefault();
41208         this.tabPanel.activate(this.id);
41209     },
41210 /*
41211     getWidth : function(){
41212         return this.inner.getWidth();
41213     },
41214
41215     setWidth : function(width){
41216         var iwidth = width - this.linode.getPadding("lr");
41217         this.inner.setWidth(iwidth);
41218         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41219         this.linode.setWidth(width);
41220     },
41221 */
41222     /**
41223      * Show or hide the tab
41224      * @param {Boolean} hidden True to hide or false to show.
41225      */
41226     setHidden : function(hidden){
41227         this.hidden = hidden;
41228         this.linode.setStyle("display", hidden ? "none" : "");
41229     },
41230
41231     /**
41232      * Returns true if this tab is "hidden"
41233      * @return {Boolean}
41234      */
41235     isHidden : function(){
41236         return this.hidden;
41237     },
41238
41239     /**
41240      * Returns the text for this tab
41241      * @return {String}
41242      */
41243     getText : function(){
41244         return this.text;
41245     },
41246     /*
41247     autoSize : function(){
41248         //this.el.beginMeasure();
41249         this.textEl.setWidth(1);
41250         /*
41251          *  #2804 [new] Tabs in Roojs
41252          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41253          */
41254         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41255         //this.el.endMeasure();
41256     //},
41257
41258     /**
41259      * Sets the text for the tab (Note: this also sets the tooltip text)
41260      * @param {String} text The tab's text and tooltip
41261      */
41262     setText : function(text){
41263         this.text = text;
41264         this.textEl.update(text);
41265         this.setTooltip(text);
41266         //if(!this.tabPanel.resizeTabs){
41267         //    this.autoSize();
41268         //}
41269     },
41270     /**
41271      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41272      */
41273     activate : function(){
41274         this.tabPanel.activate(this.id);
41275     },
41276
41277     /**
41278      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41279      */
41280     disable : function(){
41281         if(this.tabPanel.active != this){
41282             this.disabled = true;
41283             this.status_node.addClass("disabled");
41284         }
41285     },
41286
41287     /**
41288      * Enables this TabPanelItem if it was previously disabled.
41289      */
41290     enable : function(){
41291         this.disabled = false;
41292         this.status_node.removeClass("disabled");
41293     },
41294
41295     /**
41296      * Sets the content for this TabPanelItem.
41297      * @param {String} content The content
41298      * @param {Boolean} loadScripts true to look for and load scripts
41299      */
41300     setContent : function(content, loadScripts){
41301         this.bodyEl.update(content, loadScripts);
41302     },
41303
41304     /**
41305      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41306      * @return {Roo.UpdateManager} The UpdateManager
41307      */
41308     getUpdateManager : function(){
41309         return this.bodyEl.getUpdateManager();
41310     },
41311
41312     /**
41313      * Set a URL to be used to load the content for this TabPanelItem.
41314      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41315      * @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)
41316      * @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)
41317      * @return {Roo.UpdateManager} The UpdateManager
41318      */
41319     setUrl : function(url, params, loadOnce){
41320         if(this.refreshDelegate){
41321             this.un('activate', this.refreshDelegate);
41322         }
41323         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41324         this.on("activate", this.refreshDelegate);
41325         return this.bodyEl.getUpdateManager();
41326     },
41327
41328     /** @private */
41329     _handleRefresh : function(url, params, loadOnce){
41330         if(!loadOnce || !this.loaded){
41331             var updater = this.bodyEl.getUpdateManager();
41332             updater.update(url, params, this._setLoaded.createDelegate(this));
41333         }
41334     },
41335
41336     /**
41337      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41338      *   Will fail silently if the setUrl method has not been called.
41339      *   This does not activate the panel, just updates its content.
41340      */
41341     refresh : function(){
41342         if(this.refreshDelegate){
41343            this.loaded = false;
41344            this.refreshDelegate();
41345         }
41346     },
41347
41348     /** @private */
41349     _setLoaded : function(){
41350         this.loaded = true;
41351     },
41352
41353     /** @private */
41354     closeClick : function(e){
41355         var o = {};
41356         e.stopEvent();
41357         this.fireEvent("beforeclose", this, o);
41358         if(o.cancel !== true){
41359             this.tabPanel.removeTab(this.id);
41360         }
41361     },
41362     /**
41363      * The text displayed in the tooltip for the close icon.
41364      * @type String
41365      */
41366     closeText : "Close this tab"
41367 });
41368 /**
41369 *    This script refer to:
41370 *    Title: International Telephone Input
41371 *    Author: Jack O'Connor
41372 *    Code version:  v12.1.12
41373 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41374 **/
41375
41376 Roo.bootstrap.PhoneInputData = function() {
41377     var d = [
41378       [
41379         "Afghanistan (‫افغانستان‬‎)",
41380         "af",
41381         "93"
41382       ],
41383       [
41384         "Albania (Shqipëri)",
41385         "al",
41386         "355"
41387       ],
41388       [
41389         "Algeria (‫الجزائر‬‎)",
41390         "dz",
41391         "213"
41392       ],
41393       [
41394         "American Samoa",
41395         "as",
41396         "1684"
41397       ],
41398       [
41399         "Andorra",
41400         "ad",
41401         "376"
41402       ],
41403       [
41404         "Angola",
41405         "ao",
41406         "244"
41407       ],
41408       [
41409         "Anguilla",
41410         "ai",
41411         "1264"
41412       ],
41413       [
41414         "Antigua and Barbuda",
41415         "ag",
41416         "1268"
41417       ],
41418       [
41419         "Argentina",
41420         "ar",
41421         "54"
41422       ],
41423       [
41424         "Armenia (Հայաստան)",
41425         "am",
41426         "374"
41427       ],
41428       [
41429         "Aruba",
41430         "aw",
41431         "297"
41432       ],
41433       [
41434         "Australia",
41435         "au",
41436         "61",
41437         0
41438       ],
41439       [
41440         "Austria (Österreich)",
41441         "at",
41442         "43"
41443       ],
41444       [
41445         "Azerbaijan (Azərbaycan)",
41446         "az",
41447         "994"
41448       ],
41449       [
41450         "Bahamas",
41451         "bs",
41452         "1242"
41453       ],
41454       [
41455         "Bahrain (‫البحرين‬‎)",
41456         "bh",
41457         "973"
41458       ],
41459       [
41460         "Bangladesh (বাংলাদেশ)",
41461         "bd",
41462         "880"
41463       ],
41464       [
41465         "Barbados",
41466         "bb",
41467         "1246"
41468       ],
41469       [
41470         "Belarus (Беларусь)",
41471         "by",
41472         "375"
41473       ],
41474       [
41475         "Belgium (België)",
41476         "be",
41477         "32"
41478       ],
41479       [
41480         "Belize",
41481         "bz",
41482         "501"
41483       ],
41484       [
41485         "Benin (Bénin)",
41486         "bj",
41487         "229"
41488       ],
41489       [
41490         "Bermuda",
41491         "bm",
41492         "1441"
41493       ],
41494       [
41495         "Bhutan (འབྲུག)",
41496         "bt",
41497         "975"
41498       ],
41499       [
41500         "Bolivia",
41501         "bo",
41502         "591"
41503       ],
41504       [
41505         "Bosnia and Herzegovina (Босна и Херцеговина)",
41506         "ba",
41507         "387"
41508       ],
41509       [
41510         "Botswana",
41511         "bw",
41512         "267"
41513       ],
41514       [
41515         "Brazil (Brasil)",
41516         "br",
41517         "55"
41518       ],
41519       [
41520         "British Indian Ocean Territory",
41521         "io",
41522         "246"
41523       ],
41524       [
41525         "British Virgin Islands",
41526         "vg",
41527         "1284"
41528       ],
41529       [
41530         "Brunei",
41531         "bn",
41532         "673"
41533       ],
41534       [
41535         "Bulgaria (България)",
41536         "bg",
41537         "359"
41538       ],
41539       [
41540         "Burkina Faso",
41541         "bf",
41542         "226"
41543       ],
41544       [
41545         "Burundi (Uburundi)",
41546         "bi",
41547         "257"
41548       ],
41549       [
41550         "Cambodia (កម្ពុជា)",
41551         "kh",
41552         "855"
41553       ],
41554       [
41555         "Cameroon (Cameroun)",
41556         "cm",
41557         "237"
41558       ],
41559       [
41560         "Canada",
41561         "ca",
41562         "1",
41563         1,
41564         ["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"]
41565       ],
41566       [
41567         "Cape Verde (Kabu Verdi)",
41568         "cv",
41569         "238"
41570       ],
41571       [
41572         "Caribbean Netherlands",
41573         "bq",
41574         "599",
41575         1
41576       ],
41577       [
41578         "Cayman Islands",
41579         "ky",
41580         "1345"
41581       ],
41582       [
41583         "Central African Republic (République centrafricaine)",
41584         "cf",
41585         "236"
41586       ],
41587       [
41588         "Chad (Tchad)",
41589         "td",
41590         "235"
41591       ],
41592       [
41593         "Chile",
41594         "cl",
41595         "56"
41596       ],
41597       [
41598         "China (中国)",
41599         "cn",
41600         "86"
41601       ],
41602       [
41603         "Christmas Island",
41604         "cx",
41605         "61",
41606         2
41607       ],
41608       [
41609         "Cocos (Keeling) Islands",
41610         "cc",
41611         "61",
41612         1
41613       ],
41614       [
41615         "Colombia",
41616         "co",
41617         "57"
41618       ],
41619       [
41620         "Comoros (‫جزر القمر‬‎)",
41621         "km",
41622         "269"
41623       ],
41624       [
41625         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41626         "cd",
41627         "243"
41628       ],
41629       [
41630         "Congo (Republic) (Congo-Brazzaville)",
41631         "cg",
41632         "242"
41633       ],
41634       [
41635         "Cook Islands",
41636         "ck",
41637         "682"
41638       ],
41639       [
41640         "Costa Rica",
41641         "cr",
41642         "506"
41643       ],
41644       [
41645         "Côte d’Ivoire",
41646         "ci",
41647         "225"
41648       ],
41649       [
41650         "Croatia (Hrvatska)",
41651         "hr",
41652         "385"
41653       ],
41654       [
41655         "Cuba",
41656         "cu",
41657         "53"
41658       ],
41659       [
41660         "Curaçao",
41661         "cw",
41662         "599",
41663         0
41664       ],
41665       [
41666         "Cyprus (Κύπρος)",
41667         "cy",
41668         "357"
41669       ],
41670       [
41671         "Czech Republic (Česká republika)",
41672         "cz",
41673         "420"
41674       ],
41675       [
41676         "Denmark (Danmark)",
41677         "dk",
41678         "45"
41679       ],
41680       [
41681         "Djibouti",
41682         "dj",
41683         "253"
41684       ],
41685       [
41686         "Dominica",
41687         "dm",
41688         "1767"
41689       ],
41690       [
41691         "Dominican Republic (República Dominicana)",
41692         "do",
41693         "1",
41694         2,
41695         ["809", "829", "849"]
41696       ],
41697       [
41698         "Ecuador",
41699         "ec",
41700         "593"
41701       ],
41702       [
41703         "Egypt (‫مصر‬‎)",
41704         "eg",
41705         "20"
41706       ],
41707       [
41708         "El Salvador",
41709         "sv",
41710         "503"
41711       ],
41712       [
41713         "Equatorial Guinea (Guinea Ecuatorial)",
41714         "gq",
41715         "240"
41716       ],
41717       [
41718         "Eritrea",
41719         "er",
41720         "291"
41721       ],
41722       [
41723         "Estonia (Eesti)",
41724         "ee",
41725         "372"
41726       ],
41727       [
41728         "Ethiopia",
41729         "et",
41730         "251"
41731       ],
41732       [
41733         "Falkland Islands (Islas Malvinas)",
41734         "fk",
41735         "500"
41736       ],
41737       [
41738         "Faroe Islands (Føroyar)",
41739         "fo",
41740         "298"
41741       ],
41742       [
41743         "Fiji",
41744         "fj",
41745         "679"
41746       ],
41747       [
41748         "Finland (Suomi)",
41749         "fi",
41750         "358",
41751         0
41752       ],
41753       [
41754         "France",
41755         "fr",
41756         "33"
41757       ],
41758       [
41759         "French Guiana (Guyane française)",
41760         "gf",
41761         "594"
41762       ],
41763       [
41764         "French Polynesia (Polynésie française)",
41765         "pf",
41766         "689"
41767       ],
41768       [
41769         "Gabon",
41770         "ga",
41771         "241"
41772       ],
41773       [
41774         "Gambia",
41775         "gm",
41776         "220"
41777       ],
41778       [
41779         "Georgia (საქართველო)",
41780         "ge",
41781         "995"
41782       ],
41783       [
41784         "Germany (Deutschland)",
41785         "de",
41786         "49"
41787       ],
41788       [
41789         "Ghana (Gaana)",
41790         "gh",
41791         "233"
41792       ],
41793       [
41794         "Gibraltar",
41795         "gi",
41796         "350"
41797       ],
41798       [
41799         "Greece (Ελλάδα)",
41800         "gr",
41801         "30"
41802       ],
41803       [
41804         "Greenland (Kalaallit Nunaat)",
41805         "gl",
41806         "299"
41807       ],
41808       [
41809         "Grenada",
41810         "gd",
41811         "1473"
41812       ],
41813       [
41814         "Guadeloupe",
41815         "gp",
41816         "590",
41817         0
41818       ],
41819       [
41820         "Guam",
41821         "gu",
41822         "1671"
41823       ],
41824       [
41825         "Guatemala",
41826         "gt",
41827         "502"
41828       ],
41829       [
41830         "Guernsey",
41831         "gg",
41832         "44",
41833         1
41834       ],
41835       [
41836         "Guinea (Guinée)",
41837         "gn",
41838         "224"
41839       ],
41840       [
41841         "Guinea-Bissau (Guiné Bissau)",
41842         "gw",
41843         "245"
41844       ],
41845       [
41846         "Guyana",
41847         "gy",
41848         "592"
41849       ],
41850       [
41851         "Haiti",
41852         "ht",
41853         "509"
41854       ],
41855       [
41856         "Honduras",
41857         "hn",
41858         "504"
41859       ],
41860       [
41861         "Hong Kong (香港)",
41862         "hk",
41863         "852"
41864       ],
41865       [
41866         "Hungary (Magyarország)",
41867         "hu",
41868         "36"
41869       ],
41870       [
41871         "Iceland (Ísland)",
41872         "is",
41873         "354"
41874       ],
41875       [
41876         "India (भारत)",
41877         "in",
41878         "91"
41879       ],
41880       [
41881         "Indonesia",
41882         "id",
41883         "62"
41884       ],
41885       [
41886         "Iran (‫ایران‬‎)",
41887         "ir",
41888         "98"
41889       ],
41890       [
41891         "Iraq (‫العراق‬‎)",
41892         "iq",
41893         "964"
41894       ],
41895       [
41896         "Ireland",
41897         "ie",
41898         "353"
41899       ],
41900       [
41901         "Isle of Man",
41902         "im",
41903         "44",
41904         2
41905       ],
41906       [
41907         "Israel (‫ישראל‬‎)",
41908         "il",
41909         "972"
41910       ],
41911       [
41912         "Italy (Italia)",
41913         "it",
41914         "39",
41915         0
41916       ],
41917       [
41918         "Jamaica",
41919         "jm",
41920         "1876"
41921       ],
41922       [
41923         "Japan (日本)",
41924         "jp",
41925         "81"
41926       ],
41927       [
41928         "Jersey",
41929         "je",
41930         "44",
41931         3
41932       ],
41933       [
41934         "Jordan (‫الأردن‬‎)",
41935         "jo",
41936         "962"
41937       ],
41938       [
41939         "Kazakhstan (Казахстан)",
41940         "kz",
41941         "7",
41942         1
41943       ],
41944       [
41945         "Kenya",
41946         "ke",
41947         "254"
41948       ],
41949       [
41950         "Kiribati",
41951         "ki",
41952         "686"
41953       ],
41954       [
41955         "Kosovo",
41956         "xk",
41957         "383"
41958       ],
41959       [
41960         "Kuwait (‫الكويت‬‎)",
41961         "kw",
41962         "965"
41963       ],
41964       [
41965         "Kyrgyzstan (Кыргызстан)",
41966         "kg",
41967         "996"
41968       ],
41969       [
41970         "Laos (ລາວ)",
41971         "la",
41972         "856"
41973       ],
41974       [
41975         "Latvia (Latvija)",
41976         "lv",
41977         "371"
41978       ],
41979       [
41980         "Lebanon (‫لبنان‬‎)",
41981         "lb",
41982         "961"
41983       ],
41984       [
41985         "Lesotho",
41986         "ls",
41987         "266"
41988       ],
41989       [
41990         "Liberia",
41991         "lr",
41992         "231"
41993       ],
41994       [
41995         "Libya (‫ليبيا‬‎)",
41996         "ly",
41997         "218"
41998       ],
41999       [
42000         "Liechtenstein",
42001         "li",
42002         "423"
42003       ],
42004       [
42005         "Lithuania (Lietuva)",
42006         "lt",
42007         "370"
42008       ],
42009       [
42010         "Luxembourg",
42011         "lu",
42012         "352"
42013       ],
42014       [
42015         "Macau (澳門)",
42016         "mo",
42017         "853"
42018       ],
42019       [
42020         "Macedonia (FYROM) (Македонија)",
42021         "mk",
42022         "389"
42023       ],
42024       [
42025         "Madagascar (Madagasikara)",
42026         "mg",
42027         "261"
42028       ],
42029       [
42030         "Malawi",
42031         "mw",
42032         "265"
42033       ],
42034       [
42035         "Malaysia",
42036         "my",
42037         "60"
42038       ],
42039       [
42040         "Maldives",
42041         "mv",
42042         "960"
42043       ],
42044       [
42045         "Mali",
42046         "ml",
42047         "223"
42048       ],
42049       [
42050         "Malta",
42051         "mt",
42052         "356"
42053       ],
42054       [
42055         "Marshall Islands",
42056         "mh",
42057         "692"
42058       ],
42059       [
42060         "Martinique",
42061         "mq",
42062         "596"
42063       ],
42064       [
42065         "Mauritania (‫موريتانيا‬‎)",
42066         "mr",
42067         "222"
42068       ],
42069       [
42070         "Mauritius (Moris)",
42071         "mu",
42072         "230"
42073       ],
42074       [
42075         "Mayotte",
42076         "yt",
42077         "262",
42078         1
42079       ],
42080       [
42081         "Mexico (México)",
42082         "mx",
42083         "52"
42084       ],
42085       [
42086         "Micronesia",
42087         "fm",
42088         "691"
42089       ],
42090       [
42091         "Moldova (Republica Moldova)",
42092         "md",
42093         "373"
42094       ],
42095       [
42096         "Monaco",
42097         "mc",
42098         "377"
42099       ],
42100       [
42101         "Mongolia (Монгол)",
42102         "mn",
42103         "976"
42104       ],
42105       [
42106         "Montenegro (Crna Gora)",
42107         "me",
42108         "382"
42109       ],
42110       [
42111         "Montserrat",
42112         "ms",
42113         "1664"
42114       ],
42115       [
42116         "Morocco (‫المغرب‬‎)",
42117         "ma",
42118         "212",
42119         0
42120       ],
42121       [
42122         "Mozambique (Moçambique)",
42123         "mz",
42124         "258"
42125       ],
42126       [
42127         "Myanmar (Burma) (မြန်မာ)",
42128         "mm",
42129         "95"
42130       ],
42131       [
42132         "Namibia (Namibië)",
42133         "na",
42134         "264"
42135       ],
42136       [
42137         "Nauru",
42138         "nr",
42139         "674"
42140       ],
42141       [
42142         "Nepal (नेपाल)",
42143         "np",
42144         "977"
42145       ],
42146       [
42147         "Netherlands (Nederland)",
42148         "nl",
42149         "31"
42150       ],
42151       [
42152         "New Caledonia (Nouvelle-Calédonie)",
42153         "nc",
42154         "687"
42155       ],
42156       [
42157         "New Zealand",
42158         "nz",
42159         "64"
42160       ],
42161       [
42162         "Nicaragua",
42163         "ni",
42164         "505"
42165       ],
42166       [
42167         "Niger (Nijar)",
42168         "ne",
42169         "227"
42170       ],
42171       [
42172         "Nigeria",
42173         "ng",
42174         "234"
42175       ],
42176       [
42177         "Niue",
42178         "nu",
42179         "683"
42180       ],
42181       [
42182         "Norfolk Island",
42183         "nf",
42184         "672"
42185       ],
42186       [
42187         "North Korea (조선 민주주의 인민 공화국)",
42188         "kp",
42189         "850"
42190       ],
42191       [
42192         "Northern Mariana Islands",
42193         "mp",
42194         "1670"
42195       ],
42196       [
42197         "Norway (Norge)",
42198         "no",
42199         "47",
42200         0
42201       ],
42202       [
42203         "Oman (‫عُمان‬‎)",
42204         "om",
42205         "968"
42206       ],
42207       [
42208         "Pakistan (‫پاکستان‬‎)",
42209         "pk",
42210         "92"
42211       ],
42212       [
42213         "Palau",
42214         "pw",
42215         "680"
42216       ],
42217       [
42218         "Palestine (‫فلسطين‬‎)",
42219         "ps",
42220         "970"
42221       ],
42222       [
42223         "Panama (Panamá)",
42224         "pa",
42225         "507"
42226       ],
42227       [
42228         "Papua New Guinea",
42229         "pg",
42230         "675"
42231       ],
42232       [
42233         "Paraguay",
42234         "py",
42235         "595"
42236       ],
42237       [
42238         "Peru (Perú)",
42239         "pe",
42240         "51"
42241       ],
42242       [
42243         "Philippines",
42244         "ph",
42245         "63"
42246       ],
42247       [
42248         "Poland (Polska)",
42249         "pl",
42250         "48"
42251       ],
42252       [
42253         "Portugal",
42254         "pt",
42255         "351"
42256       ],
42257       [
42258         "Puerto Rico",
42259         "pr",
42260         "1",
42261         3,
42262         ["787", "939"]
42263       ],
42264       [
42265         "Qatar (‫قطر‬‎)",
42266         "qa",
42267         "974"
42268       ],
42269       [
42270         "Réunion (La Réunion)",
42271         "re",
42272         "262",
42273         0
42274       ],
42275       [
42276         "Romania (România)",
42277         "ro",
42278         "40"
42279       ],
42280       [
42281         "Russia (Россия)",
42282         "ru",
42283         "7",
42284         0
42285       ],
42286       [
42287         "Rwanda",
42288         "rw",
42289         "250"
42290       ],
42291       [
42292         "Saint Barthélemy",
42293         "bl",
42294         "590",
42295         1
42296       ],
42297       [
42298         "Saint Helena",
42299         "sh",
42300         "290"
42301       ],
42302       [
42303         "Saint Kitts and Nevis",
42304         "kn",
42305         "1869"
42306       ],
42307       [
42308         "Saint Lucia",
42309         "lc",
42310         "1758"
42311       ],
42312       [
42313         "Saint Martin (Saint-Martin (partie française))",
42314         "mf",
42315         "590",
42316         2
42317       ],
42318       [
42319         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42320         "pm",
42321         "508"
42322       ],
42323       [
42324         "Saint Vincent and the Grenadines",
42325         "vc",
42326         "1784"
42327       ],
42328       [
42329         "Samoa",
42330         "ws",
42331         "685"
42332       ],
42333       [
42334         "San Marino",
42335         "sm",
42336         "378"
42337       ],
42338       [
42339         "São Tomé and Príncipe (São Tomé e Príncipe)",
42340         "st",
42341         "239"
42342       ],
42343       [
42344         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42345         "sa",
42346         "966"
42347       ],
42348       [
42349         "Senegal (Sénégal)",
42350         "sn",
42351         "221"
42352       ],
42353       [
42354         "Serbia (Србија)",
42355         "rs",
42356         "381"
42357       ],
42358       [
42359         "Seychelles",
42360         "sc",
42361         "248"
42362       ],
42363       [
42364         "Sierra Leone",
42365         "sl",
42366         "232"
42367       ],
42368       [
42369         "Singapore",
42370         "sg",
42371         "65"
42372       ],
42373       [
42374         "Sint Maarten",
42375         "sx",
42376         "1721"
42377       ],
42378       [
42379         "Slovakia (Slovensko)",
42380         "sk",
42381         "421"
42382       ],
42383       [
42384         "Slovenia (Slovenija)",
42385         "si",
42386         "386"
42387       ],
42388       [
42389         "Solomon Islands",
42390         "sb",
42391         "677"
42392       ],
42393       [
42394         "Somalia (Soomaaliya)",
42395         "so",
42396         "252"
42397       ],
42398       [
42399         "South Africa",
42400         "za",
42401         "27"
42402       ],
42403       [
42404         "South Korea (대한민국)",
42405         "kr",
42406         "82"
42407       ],
42408       [
42409         "South Sudan (‫جنوب السودان‬‎)",
42410         "ss",
42411         "211"
42412       ],
42413       [
42414         "Spain (España)",
42415         "es",
42416         "34"
42417       ],
42418       [
42419         "Sri Lanka (ශ්‍රී ලංකාව)",
42420         "lk",
42421         "94"
42422       ],
42423       [
42424         "Sudan (‫السودان‬‎)",
42425         "sd",
42426         "249"
42427       ],
42428       [
42429         "Suriname",
42430         "sr",
42431         "597"
42432       ],
42433       [
42434         "Svalbard and Jan Mayen",
42435         "sj",
42436         "47",
42437         1
42438       ],
42439       [
42440         "Swaziland",
42441         "sz",
42442         "268"
42443       ],
42444       [
42445         "Sweden (Sverige)",
42446         "se",
42447         "46"
42448       ],
42449       [
42450         "Switzerland (Schweiz)",
42451         "ch",
42452         "41"
42453       ],
42454       [
42455         "Syria (‫سوريا‬‎)",
42456         "sy",
42457         "963"
42458       ],
42459       [
42460         "Taiwan (台灣)",
42461         "tw",
42462         "886"
42463       ],
42464       [
42465         "Tajikistan",
42466         "tj",
42467         "992"
42468       ],
42469       [
42470         "Tanzania",
42471         "tz",
42472         "255"
42473       ],
42474       [
42475         "Thailand (ไทย)",
42476         "th",
42477         "66"
42478       ],
42479       [
42480         "Timor-Leste",
42481         "tl",
42482         "670"
42483       ],
42484       [
42485         "Togo",
42486         "tg",
42487         "228"
42488       ],
42489       [
42490         "Tokelau",
42491         "tk",
42492         "690"
42493       ],
42494       [
42495         "Tonga",
42496         "to",
42497         "676"
42498       ],
42499       [
42500         "Trinidad and Tobago",
42501         "tt",
42502         "1868"
42503       ],
42504       [
42505         "Tunisia (‫تونس‬‎)",
42506         "tn",
42507         "216"
42508       ],
42509       [
42510         "Turkey (Türkiye)",
42511         "tr",
42512         "90"
42513       ],
42514       [
42515         "Turkmenistan",
42516         "tm",
42517         "993"
42518       ],
42519       [
42520         "Turks and Caicos Islands",
42521         "tc",
42522         "1649"
42523       ],
42524       [
42525         "Tuvalu",
42526         "tv",
42527         "688"
42528       ],
42529       [
42530         "U.S. Virgin Islands",
42531         "vi",
42532         "1340"
42533       ],
42534       [
42535         "Uganda",
42536         "ug",
42537         "256"
42538       ],
42539       [
42540         "Ukraine (Україна)",
42541         "ua",
42542         "380"
42543       ],
42544       [
42545         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42546         "ae",
42547         "971"
42548       ],
42549       [
42550         "United Kingdom",
42551         "gb",
42552         "44",
42553         0
42554       ],
42555       [
42556         "United States",
42557         "us",
42558         "1",
42559         0
42560       ],
42561       [
42562         "Uruguay",
42563         "uy",
42564         "598"
42565       ],
42566       [
42567         "Uzbekistan (Oʻzbekiston)",
42568         "uz",
42569         "998"
42570       ],
42571       [
42572         "Vanuatu",
42573         "vu",
42574         "678"
42575       ],
42576       [
42577         "Vatican City (Città del Vaticano)",
42578         "va",
42579         "39",
42580         1
42581       ],
42582       [
42583         "Venezuela",
42584         "ve",
42585         "58"
42586       ],
42587       [
42588         "Vietnam (Việt Nam)",
42589         "vn",
42590         "84"
42591       ],
42592       [
42593         "Wallis and Futuna (Wallis-et-Futuna)",
42594         "wf",
42595         "681"
42596       ],
42597       [
42598         "Western Sahara (‫الصحراء الغربية‬‎)",
42599         "eh",
42600         "212",
42601         1
42602       ],
42603       [
42604         "Yemen (‫اليمن‬‎)",
42605         "ye",
42606         "967"
42607       ],
42608       [
42609         "Zambia",
42610         "zm",
42611         "260"
42612       ],
42613       [
42614         "Zimbabwe",
42615         "zw",
42616         "263"
42617       ],
42618       [
42619         "Åland Islands",
42620         "ax",
42621         "358",
42622         1
42623       ]
42624   ];
42625   
42626   return d;
42627 }/**
42628 *    This script refer to:
42629 *    Title: International Telephone Input
42630 *    Author: Jack O'Connor
42631 *    Code version:  v12.1.12
42632 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42633 **/
42634
42635 /**
42636  * @class Roo.bootstrap.PhoneInput
42637  * @extends Roo.bootstrap.TriggerField
42638  * An input with International dial-code selection
42639  
42640  * @cfg {String} defaultDialCode default '+852'
42641  * @cfg {Array} preferedCountries default []
42642   
42643  * @constructor
42644  * Create a new PhoneInput.
42645  * @param {Object} config Configuration options
42646  */
42647
42648 Roo.bootstrap.PhoneInput = function(config) {
42649     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42650 };
42651
42652 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42653         
42654         listWidth: undefined,
42655         
42656         selectedClass: 'active',
42657         
42658         invalidClass : "has-warning",
42659         
42660         validClass: 'has-success',
42661         
42662         allowed: '0123456789',
42663         
42664         max_length: 15,
42665         
42666         /**
42667          * @cfg {String} defaultDialCode The default dial code when initializing the input
42668          */
42669         defaultDialCode: '+852',
42670         
42671         /**
42672          * @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
42673          */
42674         preferedCountries: false,
42675         
42676         getAutoCreate : function()
42677         {
42678             var data = Roo.bootstrap.PhoneInputData();
42679             var align = this.labelAlign || this.parentLabelAlign();
42680             var id = Roo.id();
42681             
42682             this.allCountries = [];
42683             this.dialCodeMapping = [];
42684             
42685             for (var i = 0; i < data.length; i++) {
42686               var c = data[i];
42687               this.allCountries[i] = {
42688                 name: c[0],
42689                 iso2: c[1],
42690                 dialCode: c[2],
42691                 priority: c[3] || 0,
42692                 areaCodes: c[4] || null
42693               };
42694               this.dialCodeMapping[c[2]] = {
42695                   name: c[0],
42696                   iso2: c[1],
42697                   priority: c[3] || 0,
42698                   areaCodes: c[4] || null
42699               };
42700             }
42701             
42702             var cfg = {
42703                 cls: 'form-group',
42704                 cn: []
42705             };
42706             
42707             var input =  {
42708                 tag: 'input',
42709                 id : id,
42710                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42711                 maxlength: this.max_length,
42712                 cls : 'form-control tel-input',
42713                 autocomplete: 'new-password'
42714             };
42715             
42716             var hiddenInput = {
42717                 tag: 'input',
42718                 type: 'hidden',
42719                 cls: 'hidden-tel-input'
42720             };
42721             
42722             if (this.name) {
42723                 hiddenInput.name = this.name;
42724             }
42725             
42726             if (this.disabled) {
42727                 input.disabled = true;
42728             }
42729             
42730             var flag_container = {
42731                 tag: 'div',
42732                 cls: 'flag-box',
42733                 cn: [
42734                     {
42735                         tag: 'div',
42736                         cls: 'flag'
42737                     },
42738                     {
42739                         tag: 'div',
42740                         cls: 'caret'
42741                     }
42742                 ]
42743             };
42744             
42745             var box = {
42746                 tag: 'div',
42747                 cls: this.hasFeedback ? 'has-feedback' : '',
42748                 cn: [
42749                     hiddenInput,
42750                     input,
42751                     {
42752                         tag: 'input',
42753                         cls: 'dial-code-holder',
42754                         disabled: true
42755                     }
42756                 ]
42757             };
42758             
42759             var container = {
42760                 cls: 'roo-select2-container input-group',
42761                 cn: [
42762                     flag_container,
42763                     box
42764                 ]
42765             };
42766             
42767             if (this.fieldLabel.length) {
42768                 var indicator = {
42769                     tag: 'i',
42770                     tooltip: 'This field is required'
42771                 };
42772                 
42773                 var label = {
42774                     tag: 'label',
42775                     'for':  id,
42776                     cls: 'control-label',
42777                     cn: []
42778                 };
42779                 
42780                 var label_text = {
42781                     tag: 'span',
42782                     html: this.fieldLabel
42783                 };
42784                 
42785                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42786                 label.cn = [
42787                     indicator,
42788                     label_text
42789                 ];
42790                 
42791                 if(this.indicatorpos == 'right') {
42792                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42793                     label.cn = [
42794                         label_text,
42795                         indicator
42796                     ];
42797                 }
42798                 
42799                 if(align == 'left') {
42800                     container = {
42801                         tag: 'div',
42802                         cn: [
42803                             container
42804                         ]
42805                     };
42806                     
42807                     if(this.labelWidth > 12){
42808                         label.style = "width: " + this.labelWidth + 'px';
42809                     }
42810                     if(this.labelWidth < 13 && this.labelmd == 0){
42811                         this.labelmd = this.labelWidth;
42812                     }
42813                     if(this.labellg > 0){
42814                         label.cls += ' col-lg-' + this.labellg;
42815                         input.cls += ' col-lg-' + (12 - this.labellg);
42816                     }
42817                     if(this.labelmd > 0){
42818                         label.cls += ' col-md-' + this.labelmd;
42819                         container.cls += ' col-md-' + (12 - this.labelmd);
42820                     }
42821                     if(this.labelsm > 0){
42822                         label.cls += ' col-sm-' + this.labelsm;
42823                         container.cls += ' col-sm-' + (12 - this.labelsm);
42824                     }
42825                     if(this.labelxs > 0){
42826                         label.cls += ' col-xs-' + this.labelxs;
42827                         container.cls += ' col-xs-' + (12 - this.labelxs);
42828                     }
42829                 }
42830             }
42831             
42832             cfg.cn = [
42833                 label,
42834                 container
42835             ];
42836             
42837             var settings = this;
42838             
42839             ['xs','sm','md','lg'].map(function(size){
42840                 if (settings[size]) {
42841                     cfg.cls += ' col-' + size + '-' + settings[size];
42842                 }
42843             });
42844             
42845             this.store = new Roo.data.Store({
42846                 proxy : new Roo.data.MemoryProxy({}),
42847                 reader : new Roo.data.JsonReader({
42848                     fields : [
42849                         {
42850                             'name' : 'name',
42851                             'type' : 'string'
42852                         },
42853                         {
42854                             'name' : 'iso2',
42855                             'type' : 'string'
42856                         },
42857                         {
42858                             'name' : 'dialCode',
42859                             'type' : 'string'
42860                         },
42861                         {
42862                             'name' : 'priority',
42863                             'type' : 'string'
42864                         },
42865                         {
42866                             'name' : 'areaCodes',
42867                             'type' : 'string'
42868                         }
42869                     ]
42870                 })
42871             });
42872             
42873             if(!this.preferedCountries) {
42874                 this.preferedCountries = [
42875                     'hk',
42876                     'gb',
42877                     'us'
42878                 ];
42879             }
42880             
42881             var p = this.preferedCountries.reverse();
42882             
42883             if(p) {
42884                 for (var i = 0; i < p.length; i++) {
42885                     for (var j = 0; j < this.allCountries.length; j++) {
42886                         if(this.allCountries[j].iso2 == p[i]) {
42887                             var t = this.allCountries[j];
42888                             this.allCountries.splice(j,1);
42889                             this.allCountries.unshift(t);
42890                         }
42891                     } 
42892                 }
42893             }
42894             
42895             this.store.proxy.data = {
42896                 success: true,
42897                 data: this.allCountries
42898             };
42899             
42900             return cfg;
42901         },
42902         
42903         initEvents : function()
42904         {
42905             this.createList();
42906             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42907             
42908             this.indicator = this.indicatorEl();
42909             this.flag = this.flagEl();
42910             this.dialCodeHolder = this.dialCodeHolderEl();
42911             
42912             this.trigger = this.el.select('div.flag-box',true).first();
42913             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42914             
42915             var _this = this;
42916             
42917             (function(){
42918                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42919                 _this.list.setWidth(lw);
42920             }).defer(100);
42921             
42922             this.list.on('mouseover', this.onViewOver, this);
42923             this.list.on('mousemove', this.onViewMove, this);
42924             this.inputEl().on("keyup", this.onKeyUp, this);
42925             this.inputEl().on("keypress", this.onKeyPress, this);
42926             
42927             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42928
42929             this.view = new Roo.View(this.list, this.tpl, {
42930                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42931             });
42932             
42933             this.view.on('click', this.onViewClick, this);
42934             this.setValue(this.defaultDialCode);
42935         },
42936         
42937         onTriggerClick : function(e)
42938         {
42939             Roo.log('trigger click');
42940             if(this.disabled){
42941                 return;
42942             }
42943             
42944             if(this.isExpanded()){
42945                 this.collapse();
42946                 this.hasFocus = false;
42947             }else {
42948                 this.store.load({});
42949                 this.hasFocus = true;
42950                 this.expand();
42951             }
42952         },
42953         
42954         isExpanded : function()
42955         {
42956             return this.list.isVisible();
42957         },
42958         
42959         collapse : function()
42960         {
42961             if(!this.isExpanded()){
42962                 return;
42963             }
42964             this.list.hide();
42965             Roo.get(document).un('mousedown', this.collapseIf, this);
42966             Roo.get(document).un('mousewheel', this.collapseIf, this);
42967             this.fireEvent('collapse', this);
42968             this.validate();
42969         },
42970         
42971         expand : function()
42972         {
42973             Roo.log('expand');
42974
42975             if(this.isExpanded() || !this.hasFocus){
42976                 return;
42977             }
42978             
42979             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42980             this.list.setWidth(lw);
42981             
42982             this.list.show();
42983             this.restrictHeight();
42984             
42985             Roo.get(document).on('mousedown', this.collapseIf, this);
42986             Roo.get(document).on('mousewheel', this.collapseIf, this);
42987             
42988             this.fireEvent('expand', this);
42989         },
42990         
42991         restrictHeight : function()
42992         {
42993             this.list.alignTo(this.inputEl(), this.listAlign);
42994             this.list.alignTo(this.inputEl(), this.listAlign);
42995         },
42996         
42997         onViewOver : function(e, t)
42998         {
42999             if(this.inKeyMode){
43000                 return;
43001             }
43002             var item = this.view.findItemFromChild(t);
43003             
43004             if(item){
43005                 var index = this.view.indexOf(item);
43006                 this.select(index, false);
43007             }
43008         },
43009
43010         // private
43011         onViewClick : function(view, doFocus, el, e)
43012         {
43013             var index = this.view.getSelectedIndexes()[0];
43014             
43015             var r = this.store.getAt(index);
43016             
43017             if(r){
43018                 this.onSelect(r, index);
43019             }
43020             if(doFocus !== false && !this.blockFocus){
43021                 this.inputEl().focus();
43022             }
43023         },
43024         
43025         onViewMove : function(e, t)
43026         {
43027             this.inKeyMode = false;
43028         },
43029         
43030         select : function(index, scrollIntoView)
43031         {
43032             this.selectedIndex = index;
43033             this.view.select(index);
43034             if(scrollIntoView !== false){
43035                 var el = this.view.getNode(index);
43036                 if(el){
43037                     this.list.scrollChildIntoView(el, false);
43038                 }
43039             }
43040         },
43041         
43042         createList : function()
43043         {
43044             this.list = Roo.get(document.body).createChild({
43045                 tag: 'ul',
43046                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43047                 style: 'display:none'
43048             });
43049             
43050             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43051         },
43052         
43053         collapseIf : function(e)
43054         {
43055             var in_combo  = e.within(this.el);
43056             var in_list =  e.within(this.list);
43057             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43058             
43059             if (in_combo || in_list || is_list) {
43060                 return;
43061             }
43062             this.collapse();
43063         },
43064         
43065         onSelect : function(record, index)
43066         {
43067             if(this.fireEvent('beforeselect', this, record, index) !== false){
43068                 
43069                 this.setFlagClass(record.data.iso2);
43070                 this.setDialCode(record.data.dialCode);
43071                 this.hasFocus = false;
43072                 this.collapse();
43073                 this.fireEvent('select', this, record, index);
43074             }
43075         },
43076         
43077         flagEl : function()
43078         {
43079             var flag = this.el.select('div.flag',true).first();
43080             if(!flag){
43081                 return false;
43082             }
43083             return flag;
43084         },
43085         
43086         dialCodeHolderEl : function()
43087         {
43088             var d = this.el.select('input.dial-code-holder',true).first();
43089             if(!d){
43090                 return false;
43091             }
43092             return d;
43093         },
43094         
43095         setDialCode : function(v)
43096         {
43097             this.dialCodeHolder.dom.value = '+'+v;
43098         },
43099         
43100         setFlagClass : function(n)
43101         {
43102             this.flag.dom.className = 'flag '+n;
43103         },
43104         
43105         getValue : function()
43106         {
43107             var v = this.inputEl().getValue();
43108             if(this.dialCodeHolder) {
43109                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43110             }
43111             return v;
43112         },
43113         
43114         setValue : function(v)
43115         {
43116             var d = this.getDialCode(v);
43117             
43118             //invalid dial code
43119             if(v.length == 0 || !d || d.length == 0) {
43120                 if(this.rendered){
43121                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43122                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43123                 }
43124                 return;
43125             }
43126             
43127             //valid dial code
43128             this.setFlagClass(this.dialCodeMapping[d].iso2);
43129             this.setDialCode(d);
43130             this.inputEl().dom.value = v.replace('+'+d,'');
43131             this.hiddenEl().dom.value = this.getValue();
43132             
43133             this.validate();
43134         },
43135         
43136         getDialCode : function(v)
43137         {
43138             v = v ||  '';
43139             
43140             if (v.length == 0) {
43141                 return this.dialCodeHolder.dom.value;
43142             }
43143             
43144             var dialCode = "";
43145             if (v.charAt(0) != "+") {
43146                 return false;
43147             }
43148             var numericChars = "";
43149             for (var i = 1; i < v.length; i++) {
43150               var c = v.charAt(i);
43151               if (!isNaN(c)) {
43152                 numericChars += c;
43153                 if (this.dialCodeMapping[numericChars]) {
43154                   dialCode = v.substr(1, i);
43155                 }
43156                 if (numericChars.length == 4) {
43157                   break;
43158                 }
43159               }
43160             }
43161             return dialCode;
43162         },
43163         
43164         reset : function()
43165         {
43166             this.setValue(this.defaultDialCode);
43167             this.validate();
43168         },
43169         
43170         hiddenEl : function()
43171         {
43172             return this.el.select('input.hidden-tel-input',true).first();
43173         },
43174         
43175         // after setting val
43176         onKeyUp : function(e){
43177             this.setValue(this.getValue());
43178         },
43179         
43180         onKeyPress : function(e){
43181             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43182                 e.stopEvent();
43183             }
43184         }
43185         
43186 });
43187 /**
43188  * @class Roo.bootstrap.MoneyField
43189  * @extends Roo.bootstrap.ComboBox
43190  * Bootstrap MoneyField class
43191  * 
43192  * @constructor
43193  * Create a new MoneyField.
43194  * @param {Object} config Configuration options
43195  */
43196
43197 Roo.bootstrap.MoneyField = function(config) {
43198     
43199     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43200     
43201 };
43202
43203 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43204     
43205     /**
43206      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43207      */
43208     allowDecimals : true,
43209     /**
43210      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43211      */
43212     decimalSeparator : ".",
43213     /**
43214      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43215      */
43216     decimalPrecision : 0,
43217     /**
43218      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43219      */
43220     allowNegative : true,
43221     /**
43222      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43223      */
43224     allowZero: true,
43225     /**
43226      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43227      */
43228     minValue : Number.NEGATIVE_INFINITY,
43229     /**
43230      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43231      */
43232     maxValue : Number.MAX_VALUE,
43233     /**
43234      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43235      */
43236     minText : "The minimum value for this field is {0}",
43237     /**
43238      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43239      */
43240     maxText : "The maximum value for this field is {0}",
43241     /**
43242      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43243      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43244      */
43245     nanText : "{0} is not a valid number",
43246     /**
43247      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43248      */
43249     castInt : true,
43250     /**
43251      * @cfg {String} defaults currency of the MoneyField
43252      * value should be in lkey
43253      */
43254     defaultCurrency : false,
43255     /**
43256      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43257      */
43258     thousandsDelimiter : false,
43259     /**
43260      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43261      */
43262     max_length: false,
43263     
43264     inputlg : 9,
43265     inputmd : 9,
43266     inputsm : 9,
43267     inputxs : 6,
43268     
43269     store : false,
43270     
43271     getAutoCreate : function()
43272     {
43273         var align = this.labelAlign || this.parentLabelAlign();
43274         
43275         var id = Roo.id();
43276
43277         var cfg = {
43278             cls: 'form-group',
43279             cn: []
43280         };
43281
43282         var input =  {
43283             tag: 'input',
43284             id : id,
43285             cls : 'form-control roo-money-amount-input',
43286             autocomplete: 'new-password'
43287         };
43288         
43289         var hiddenInput = {
43290             tag: 'input',
43291             type: 'hidden',
43292             id: Roo.id(),
43293             cls: 'hidden-number-input'
43294         };
43295         
43296         if(this.max_length) {
43297             input.maxlength = this.max_length; 
43298         }
43299         
43300         if (this.name) {
43301             hiddenInput.name = this.name;
43302         }
43303
43304         if (this.disabled) {
43305             input.disabled = true;
43306         }
43307
43308         var clg = 12 - this.inputlg;
43309         var cmd = 12 - this.inputmd;
43310         var csm = 12 - this.inputsm;
43311         var cxs = 12 - this.inputxs;
43312         
43313         var container = {
43314             tag : 'div',
43315             cls : 'row roo-money-field',
43316             cn : [
43317                 {
43318                     tag : 'div',
43319                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43320                     cn : [
43321                         {
43322                             tag : 'div',
43323                             cls: 'roo-select2-container input-group',
43324                             cn: [
43325                                 {
43326                                     tag : 'input',
43327                                     cls : 'form-control roo-money-currency-input',
43328                                     autocomplete: 'new-password',
43329                                     readOnly : 1,
43330                                     name : this.currencyName
43331                                 },
43332                                 {
43333                                     tag :'span',
43334                                     cls : 'input-group-addon',
43335                                     cn : [
43336                                         {
43337                                             tag: 'span',
43338                                             cls: 'caret'
43339                                         }
43340                                     ]
43341                                 }
43342                             ]
43343                         }
43344                     ]
43345                 },
43346                 {
43347                     tag : 'div',
43348                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43349                     cn : [
43350                         {
43351                             tag: 'div',
43352                             cls: this.hasFeedback ? 'has-feedback' : '',
43353                             cn: [
43354                                 input
43355                             ]
43356                         }
43357                     ]
43358                 }
43359             ]
43360             
43361         };
43362         
43363         if (this.fieldLabel.length) {
43364             var indicator = {
43365                 tag: 'i',
43366                 tooltip: 'This field is required'
43367             };
43368
43369             var label = {
43370                 tag: 'label',
43371                 'for':  id,
43372                 cls: 'control-label',
43373                 cn: []
43374             };
43375
43376             var label_text = {
43377                 tag: 'span',
43378                 html: this.fieldLabel
43379             };
43380
43381             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43382             label.cn = [
43383                 indicator,
43384                 label_text
43385             ];
43386
43387             if(this.indicatorpos == 'right') {
43388                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43389                 label.cn = [
43390                     label_text,
43391                     indicator
43392                 ];
43393             }
43394
43395             if(align == 'left') {
43396                 container = {
43397                     tag: 'div',
43398                     cn: [
43399                         container
43400                     ]
43401                 };
43402
43403                 if(this.labelWidth > 12){
43404                     label.style = "width: " + this.labelWidth + 'px';
43405                 }
43406                 if(this.labelWidth < 13 && this.labelmd == 0){
43407                     this.labelmd = this.labelWidth;
43408                 }
43409                 if(this.labellg > 0){
43410                     label.cls += ' col-lg-' + this.labellg;
43411                     input.cls += ' col-lg-' + (12 - this.labellg);
43412                 }
43413                 if(this.labelmd > 0){
43414                     label.cls += ' col-md-' + this.labelmd;
43415                     container.cls += ' col-md-' + (12 - this.labelmd);
43416                 }
43417                 if(this.labelsm > 0){
43418                     label.cls += ' col-sm-' + this.labelsm;
43419                     container.cls += ' col-sm-' + (12 - this.labelsm);
43420                 }
43421                 if(this.labelxs > 0){
43422                     label.cls += ' col-xs-' + this.labelxs;
43423                     container.cls += ' col-xs-' + (12 - this.labelxs);
43424                 }
43425             }
43426         }
43427
43428         cfg.cn = [
43429             label,
43430             container,
43431             hiddenInput
43432         ];
43433         
43434         var settings = this;
43435
43436         ['xs','sm','md','lg'].map(function(size){
43437             if (settings[size]) {
43438                 cfg.cls += ' col-' + size + '-' + settings[size];
43439             }
43440         });
43441         
43442         return cfg;
43443     },
43444     
43445     initEvents : function()
43446     {
43447         this.indicator = this.indicatorEl();
43448         
43449         this.initCurrencyEvent();
43450         
43451         this.initNumberEvent();
43452     },
43453     
43454     initCurrencyEvent : function()
43455     {
43456         if (!this.store) {
43457             throw "can not find store for combo";
43458         }
43459         
43460         this.store = Roo.factory(this.store, Roo.data);
43461         this.store.parent = this;
43462         
43463         this.createList();
43464         
43465         this.triggerEl = this.el.select('.input-group-addon', true).first();
43466         
43467         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43468         
43469         var _this = this;
43470         
43471         (function(){
43472             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43473             _this.list.setWidth(lw);
43474         }).defer(100);
43475         
43476         this.list.on('mouseover', this.onViewOver, this);
43477         this.list.on('mousemove', this.onViewMove, this);
43478         this.list.on('scroll', this.onViewScroll, this);
43479         
43480         if(!this.tpl){
43481             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43482         }
43483         
43484         this.view = new Roo.View(this.list, this.tpl, {
43485             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43486         });
43487         
43488         this.view.on('click', this.onViewClick, this);
43489         
43490         this.store.on('beforeload', this.onBeforeLoad, this);
43491         this.store.on('load', this.onLoad, this);
43492         this.store.on('loadexception', this.onLoadException, this);
43493         
43494         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43495             "up" : function(e){
43496                 this.inKeyMode = true;
43497                 this.selectPrev();
43498             },
43499
43500             "down" : function(e){
43501                 if(!this.isExpanded()){
43502                     this.onTriggerClick();
43503                 }else{
43504                     this.inKeyMode = true;
43505                     this.selectNext();
43506                 }
43507             },
43508
43509             "enter" : function(e){
43510                 this.collapse();
43511                 
43512                 if(this.fireEvent("specialkey", this, e)){
43513                     this.onViewClick(false);
43514                 }
43515                 
43516                 return true;
43517             },
43518
43519             "esc" : function(e){
43520                 this.collapse();
43521             },
43522
43523             "tab" : function(e){
43524                 this.collapse();
43525                 
43526                 if(this.fireEvent("specialkey", this, e)){
43527                     this.onViewClick(false);
43528                 }
43529                 
43530                 return true;
43531             },
43532
43533             scope : this,
43534
43535             doRelay : function(foo, bar, hname){
43536                 if(hname == 'down' || this.scope.isExpanded()){
43537                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43538                 }
43539                 return true;
43540             },
43541
43542             forceKeyDown: true
43543         });
43544         
43545         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43546         
43547     },
43548     
43549     initNumberEvent : function(e)
43550     {
43551         this.inputEl().on("keydown" , this.fireKey,  this);
43552         this.inputEl().on("focus", this.onFocus,  this);
43553         this.inputEl().on("blur", this.onBlur,  this);
43554         
43555         this.inputEl().relayEvent('keyup', this);
43556         
43557         if(this.indicator){
43558             this.indicator.addClass('invisible');
43559         }
43560  
43561         this.originalValue = this.getValue();
43562         
43563         if(this.validationEvent == 'keyup'){
43564             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43565             this.inputEl().on('keyup', this.filterValidation, this);
43566         }
43567         else if(this.validationEvent !== false){
43568             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43569         }
43570         
43571         if(this.selectOnFocus){
43572             this.on("focus", this.preFocus, this);
43573             
43574         }
43575         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43576             this.inputEl().on("keypress", this.filterKeys, this);
43577         } else {
43578             this.inputEl().relayEvent('keypress', this);
43579         }
43580         
43581         var allowed = "0123456789";
43582         
43583         if(this.allowDecimals){
43584             allowed += this.decimalSeparator;
43585         }
43586         
43587         if(this.allowNegative){
43588             allowed += "-";
43589         }
43590         
43591         if(this.thousandsDelimiter) {
43592             allowed += ",";
43593         }
43594         
43595         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43596         
43597         var keyPress = function(e){
43598             
43599             var k = e.getKey();
43600             
43601             var c = e.getCharCode();
43602             
43603             if(
43604                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43605                     allowed.indexOf(String.fromCharCode(c)) === -1
43606             ){
43607                 e.stopEvent();
43608                 return;
43609             }
43610             
43611             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43612                 return;
43613             }
43614             
43615             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43616                 e.stopEvent();
43617             }
43618         };
43619         
43620         this.inputEl().on("keypress", keyPress, this);
43621         
43622     },
43623     
43624     onTriggerClick : function(e)
43625     {   
43626         if(this.disabled){
43627             return;
43628         }
43629         
43630         this.page = 0;
43631         this.loadNext = false;
43632         
43633         if(this.isExpanded()){
43634             this.collapse();
43635             return;
43636         }
43637         
43638         this.hasFocus = true;
43639         
43640         if(this.triggerAction == 'all') {
43641             this.doQuery(this.allQuery, true);
43642             return;
43643         }
43644         
43645         this.doQuery(this.getRawValue());
43646     },
43647     
43648     getCurrency : function()
43649     {   
43650         var v = this.currencyEl().getValue();
43651         
43652         return v;
43653     },
43654     
43655     restrictHeight : function()
43656     {
43657         this.list.alignTo(this.currencyEl(), this.listAlign);
43658         this.list.alignTo(this.currencyEl(), this.listAlign);
43659     },
43660     
43661     onViewClick : function(view, doFocus, el, e)
43662     {
43663         var index = this.view.getSelectedIndexes()[0];
43664         
43665         var r = this.store.getAt(index);
43666         
43667         if(r){
43668             this.onSelect(r, index);
43669         }
43670     },
43671     
43672     onSelect : function(record, index){
43673         
43674         if(this.fireEvent('beforeselect', this, record, index) !== false){
43675         
43676             this.setFromCurrencyData(index > -1 ? record.data : false);
43677             
43678             this.collapse();
43679             
43680             this.fireEvent('select', this, record, index);
43681         }
43682     },
43683     
43684     setFromCurrencyData : function(o)
43685     {
43686         var currency = '';
43687         
43688         this.lastCurrency = o;
43689         
43690         if (this.currencyField) {
43691             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43692         } else {
43693             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43694         }
43695         
43696         this.lastSelectionText = currency;
43697         
43698         //setting default currency
43699         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43700             this.setCurrency(this.defaultCurrency);
43701             return;
43702         }
43703         
43704         this.setCurrency(currency);
43705     },
43706     
43707     setFromData : function(o)
43708     {
43709         var c = {};
43710         
43711         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43712         
43713         this.setFromCurrencyData(c);
43714         
43715         var value = '';
43716         
43717         if (this.name) {
43718             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43719         } else {
43720             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43721         }
43722         
43723         this.setValue(value);
43724         
43725     },
43726     
43727     setCurrency : function(v)
43728     {   
43729         this.currencyValue = v;
43730         
43731         if(this.rendered){
43732             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43733             this.validate();
43734         }
43735     },
43736     
43737     setValue : function(v)
43738     {
43739         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43740         
43741         this.value = v;
43742         
43743         if(this.rendered){
43744             
43745             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43746             
43747             this.inputEl().dom.value = (v == '') ? '' :
43748                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43749             
43750             if(!this.allowZero && v === '0') {
43751                 this.hiddenEl().dom.value = '';
43752                 this.inputEl().dom.value = '';
43753             }
43754             
43755             this.validate();
43756         }
43757     },
43758     
43759     getRawValue : function()
43760     {
43761         var v = this.inputEl().getValue();
43762         
43763         return v;
43764     },
43765     
43766     getValue : function()
43767     {
43768         return this.fixPrecision(this.parseValue(this.getRawValue()));
43769     },
43770     
43771     parseValue : function(value)
43772     {
43773         if(this.thousandsDelimiter) {
43774             value += "";
43775             r = new RegExp(",", "g");
43776             value = value.replace(r, "");
43777         }
43778         
43779         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43780         return isNaN(value) ? '' : value;
43781         
43782     },
43783     
43784     fixPrecision : function(value)
43785     {
43786         if(this.thousandsDelimiter) {
43787             value += "";
43788             r = new RegExp(",", "g");
43789             value = value.replace(r, "");
43790         }
43791         
43792         var nan = isNaN(value);
43793         
43794         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43795             return nan ? '' : value;
43796         }
43797         return parseFloat(value).toFixed(this.decimalPrecision);
43798     },
43799     
43800     decimalPrecisionFcn : function(v)
43801     {
43802         return Math.floor(v);
43803     },
43804     
43805     validateValue : function(value)
43806     {
43807         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43808             return false;
43809         }
43810         
43811         var num = this.parseValue(value);
43812         
43813         if(isNaN(num)){
43814             this.markInvalid(String.format(this.nanText, value));
43815             return false;
43816         }
43817         
43818         if(num < this.minValue){
43819             this.markInvalid(String.format(this.minText, this.minValue));
43820             return false;
43821         }
43822         
43823         if(num > this.maxValue){
43824             this.markInvalid(String.format(this.maxText, this.maxValue));
43825             return false;
43826         }
43827         
43828         return true;
43829     },
43830     
43831     validate : function()
43832     {
43833         if(this.disabled || this.allowBlank){
43834             this.markValid();
43835             return true;
43836         }
43837         
43838         var currency = this.getCurrency();
43839         
43840         if(this.validateValue(this.getRawValue()) && currency.length){
43841             this.markValid();
43842             return true;
43843         }
43844         
43845         this.markInvalid();
43846         return false;
43847     },
43848     
43849     getName: function()
43850     {
43851         return this.name;
43852     },
43853     
43854     beforeBlur : function()
43855     {
43856         if(!this.castInt){
43857             return;
43858         }
43859         
43860         var v = this.parseValue(this.getRawValue());
43861         
43862         if(v || v == 0){
43863             this.setValue(v);
43864         }
43865     },
43866     
43867     onBlur : function()
43868     {
43869         this.beforeBlur();
43870         
43871         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43872             //this.el.removeClass(this.focusClass);
43873         }
43874         
43875         this.hasFocus = false;
43876         
43877         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43878             this.validate();
43879         }
43880         
43881         var v = this.getValue();
43882         
43883         if(String(v) !== String(this.startValue)){
43884             this.fireEvent('change', this, v, this.startValue);
43885         }
43886         
43887         this.fireEvent("blur", this);
43888     },
43889     
43890     inputEl : function()
43891     {
43892         return this.el.select('.roo-money-amount-input', true).first();
43893     },
43894     
43895     currencyEl : function()
43896     {
43897         return this.el.select('.roo-money-currency-input', true).first();
43898     },
43899     
43900     hiddenEl : function()
43901     {
43902         return this.el.select('input.hidden-number-input',true).first();
43903     }
43904     
43905 });/**
43906  * @class Roo.bootstrap.BezierSignature
43907  * @extends Roo.bootstrap.Component
43908  * Bootstrap BezierSignature class
43909  * This script refer to:
43910  *    Title: Signature Pad
43911  *    Author: szimek
43912  *    Availability: https://github.com/szimek/signature_pad
43913  *
43914  * @constructor
43915  * Create a new BezierSignature
43916  * @param {Object} config The config object
43917  */
43918
43919 Roo.bootstrap.BezierSignature = function(config){
43920     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43921     this.addEvents({
43922         "resize" : true
43923     });
43924 };
43925
43926 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43927 {
43928      
43929     curve_data: [],
43930     
43931     is_empty: true,
43932     
43933     mouse_btn_down: true,
43934     
43935     /**
43936      * @cfg {int} canvas height
43937      */
43938     canvas_height: '200px',
43939     
43940     /**
43941      * @cfg {float|function} Radius of a single dot.
43942      */ 
43943     dot_size: false,
43944     
43945     /**
43946      * @cfg {float} Minimum width of a line. Defaults to 0.5.
43947      */
43948     min_width: 0.5,
43949     
43950     /**
43951      * @cfg {float} Maximum width of a line. Defaults to 2.5.
43952      */
43953     max_width: 2.5,
43954     
43955     /**
43956      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43957      */
43958     throttle: 16,
43959     
43960     /**
43961      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43962      */
43963     min_distance: 5,
43964     
43965     /**
43966      * @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.
43967      */
43968     bg_color: 'rgba(0, 0, 0, 0)',
43969     
43970     /**
43971      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43972      */
43973     dot_color: 'black',
43974     
43975     /**
43976      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43977      */ 
43978     velocity_filter_weight: 0.7,
43979     
43980     /**
43981      * @cfg {function} Callback when stroke begin. 
43982      */
43983     onBegin: false,
43984     
43985     /**
43986      * @cfg {function} Callback when stroke end.
43987      */
43988     onEnd: false,
43989     
43990     getAutoCreate : function()
43991     {
43992         var cls = 'roo-signature column';
43993         
43994         if(this.cls){
43995             cls += ' ' + this.cls;
43996         }
43997         
43998         var col_sizes = [
43999             'lg',
44000             'md',
44001             'sm',
44002             'xs'
44003         ];
44004         
44005         for(var i = 0; i < col_sizes.length; i++) {
44006             if(this[col_sizes[i]]) {
44007                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44008             }
44009         }
44010         
44011         var cfg = {
44012             tag: 'div',
44013             cls: cls,
44014             cn: [
44015                 {
44016                     tag: 'div',
44017                     cls: 'roo-signature-body',
44018                     cn: [
44019                         {
44020                             tag: 'canvas',
44021                             cls: 'roo-signature-body-canvas',
44022                             height: this.canvas_height,
44023                             width: this.canvas_width
44024                         }
44025                     ]
44026                 },
44027                 {
44028                     tag: 'input',
44029                     type: 'file',
44030                     style: 'display: none'
44031                 }
44032             ]
44033         };
44034         
44035         return cfg;
44036     },
44037     
44038     initEvents: function() 
44039     {
44040         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44041         
44042         var canvas = this.canvasEl();
44043         
44044         // mouse && touch event swapping...
44045         canvas.dom.style.touchAction = 'none';
44046         canvas.dom.style.msTouchAction = 'none';
44047         
44048         this.mouse_btn_down = false;
44049         canvas.on('mousedown', this._handleMouseDown, this);
44050         canvas.on('mousemove', this._handleMouseMove, this);
44051         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44052         
44053         if (window.PointerEvent) {
44054             canvas.on('pointerdown', this._handleMouseDown, this);
44055             canvas.on('pointermove', this._handleMouseMove, this);
44056             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44057         }
44058         
44059         if ('ontouchstart' in window) {
44060             canvas.on('touchstart', this._handleTouchStart, this);
44061             canvas.on('touchmove', this._handleTouchMove, this);
44062             canvas.on('touchend', this._handleTouchEnd, this);
44063         }
44064         
44065         Roo.EventManager.onWindowResize(this.resize, this, true);
44066         
44067         // file input event
44068         this.fileEl().on('change', this.uploadImage, this);
44069         
44070         this.clear();
44071         
44072         this.resize();
44073     },
44074     
44075     resize: function(){
44076         
44077         var canvas = this.canvasEl().dom;
44078         var ctx = this.canvasElCtx();
44079         var img_data = false;
44080         
44081         if(canvas.width > 0) {
44082             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44083         }
44084         // setting canvas width will clean img data
44085         canvas.width = 0;
44086         
44087         var style = window.getComputedStyle ? 
44088             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44089             
44090         var padding_left = parseInt(style.paddingLeft) || 0;
44091         var padding_right = parseInt(style.paddingRight) || 0;
44092         
44093         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44094         
44095         if(img_data) {
44096             ctx.putImageData(img_data, 0, 0);
44097         }
44098     },
44099     
44100     _handleMouseDown: function(e)
44101     {
44102         if (e.browserEvent.which === 1) {
44103             this.mouse_btn_down = true;
44104             this.strokeBegin(e);
44105         }
44106     },
44107     
44108     _handleMouseMove: function (e)
44109     {
44110         if (this.mouse_btn_down) {
44111             this.strokeMoveUpdate(e);
44112         }
44113     },
44114     
44115     _handleMouseUp: function (e)
44116     {
44117         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44118             this.mouse_btn_down = false;
44119             this.strokeEnd(e);
44120         }
44121     },
44122     
44123     _handleTouchStart: function (e) {
44124         
44125         e.preventDefault();
44126         if (e.browserEvent.targetTouches.length === 1) {
44127             // var touch = e.browserEvent.changedTouches[0];
44128             // this.strokeBegin(touch);
44129             
44130              this.strokeBegin(e); // assume e catching the correct xy...
44131         }
44132     },
44133     
44134     _handleTouchMove: function (e) {
44135         e.preventDefault();
44136         // var touch = event.targetTouches[0];
44137         // _this._strokeMoveUpdate(touch);
44138         this.strokeMoveUpdate(e);
44139     },
44140     
44141     _handleTouchEnd: function (e) {
44142         var wasCanvasTouched = e.target === this.canvasEl().dom;
44143         if (wasCanvasTouched) {
44144             e.preventDefault();
44145             // var touch = event.changedTouches[0];
44146             // _this._strokeEnd(touch);
44147             this.strokeEnd(e);
44148         }
44149     },
44150     
44151     reset: function () {
44152         this._lastPoints = [];
44153         this._lastVelocity = 0;
44154         this._lastWidth = (this.min_width + this.max_width) / 2;
44155         this.canvasElCtx().fillStyle = this.dot_color;
44156     },
44157     
44158     strokeMoveUpdate: function(e)
44159     {
44160         this.strokeUpdate(e);
44161         
44162         if (this.throttle) {
44163             this.throttleStroke(this.strokeUpdate, this.throttle);
44164         }
44165         else {
44166             this.strokeUpdate(e);
44167         }
44168     },
44169     
44170     strokeBegin: function(e)
44171     {
44172         var newPointGroup = {
44173             color: this.dot_color,
44174             points: []
44175         };
44176         
44177         if (typeof this.onBegin === 'function') {
44178             this.onBegin(e);
44179         }
44180         
44181         this.curve_data.push(newPointGroup);
44182         this.reset();
44183         this.strokeUpdate(e);
44184     },
44185     
44186     strokeUpdate: function(e)
44187     {
44188         var rect = this.canvasEl().dom.getBoundingClientRect();
44189         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44190         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44191         var lastPoints = lastPointGroup.points;
44192         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44193         var isLastPointTooClose = lastPoint
44194             ? point.distanceTo(lastPoint) <= this.min_distance
44195             : false;
44196         var color = lastPointGroup.color;
44197         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44198             var curve = this.addPoint(point);
44199             if (!lastPoint) {
44200                 this.drawDot({color: color, point: point});
44201             }
44202             else if (curve) {
44203                 this.drawCurve({color: color, curve: curve});
44204             }
44205             lastPoints.push({
44206                 time: point.time,
44207                 x: point.x,
44208                 y: point.y
44209             });
44210         }
44211     },
44212     
44213     strokeEnd: function(e)
44214     {
44215         this.strokeUpdate(e);
44216         if (typeof this.onEnd === 'function') {
44217             this.onEnd(e);
44218         }
44219     },
44220     
44221     addPoint:  function (point) {
44222         var _lastPoints = this._lastPoints;
44223         _lastPoints.push(point);
44224         if (_lastPoints.length > 2) {
44225             if (_lastPoints.length === 3) {
44226                 _lastPoints.unshift(_lastPoints[0]);
44227             }
44228             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44229             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44230             _lastPoints.shift();
44231             return curve;
44232         }
44233         return null;
44234     },
44235     
44236     calculateCurveWidths: function (startPoint, endPoint) {
44237         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44238             (1 - this.velocity_filter_weight) * this._lastVelocity;
44239
44240         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44241         var widths = {
44242             end: newWidth,
44243             start: this._lastWidth
44244         };
44245         
44246         this._lastVelocity = velocity;
44247         this._lastWidth = newWidth;
44248         return widths;
44249     },
44250     
44251     drawDot: function (_a) {
44252         var color = _a.color, point = _a.point;
44253         var ctx = this.canvasElCtx();
44254         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44255         ctx.beginPath();
44256         this.drawCurveSegment(point.x, point.y, width);
44257         ctx.closePath();
44258         ctx.fillStyle = color;
44259         ctx.fill();
44260     },
44261     
44262     drawCurve: function (_a) {
44263         var color = _a.color, curve = _a.curve;
44264         var ctx = this.canvasElCtx();
44265         var widthDelta = curve.endWidth - curve.startWidth;
44266         var drawSteps = Math.floor(curve.length()) * 2;
44267         ctx.beginPath();
44268         ctx.fillStyle = color;
44269         for (var i = 0; i < drawSteps; i += 1) {
44270         var t = i / drawSteps;
44271         var tt = t * t;
44272         var ttt = tt * t;
44273         var u = 1 - t;
44274         var uu = u * u;
44275         var uuu = uu * u;
44276         var x = uuu * curve.startPoint.x;
44277         x += 3 * uu * t * curve.control1.x;
44278         x += 3 * u * tt * curve.control2.x;
44279         x += ttt * curve.endPoint.x;
44280         var y = uuu * curve.startPoint.y;
44281         y += 3 * uu * t * curve.control1.y;
44282         y += 3 * u * tt * curve.control2.y;
44283         y += ttt * curve.endPoint.y;
44284         var width = curve.startWidth + ttt * widthDelta;
44285         this.drawCurveSegment(x, y, width);
44286         }
44287         ctx.closePath();
44288         ctx.fill();
44289     },
44290     
44291     drawCurveSegment: function (x, y, width) {
44292         var ctx = this.canvasElCtx();
44293         ctx.moveTo(x, y);
44294         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44295         this.is_empty = false;
44296     },
44297     
44298     clear: function()
44299     {
44300         var ctx = this.canvasElCtx();
44301         var canvas = this.canvasEl().dom;
44302         ctx.fillStyle = this.bg_color;
44303         ctx.clearRect(0, 0, canvas.width, canvas.height);
44304         ctx.fillRect(0, 0, canvas.width, canvas.height);
44305         this.curve_data = [];
44306         this.reset();
44307         this.is_empty = true;
44308     },
44309     
44310     fileEl: function()
44311     {
44312         return  this.el.select('input',true).first();
44313     },
44314     
44315     canvasEl: function()
44316     {
44317         return this.el.select('canvas',true).first();
44318     },
44319     
44320     canvasElCtx: function()
44321     {
44322         return this.el.select('canvas',true).first().dom.getContext('2d');
44323     },
44324     
44325     getImage: function(type)
44326     {
44327         if(this.is_empty) {
44328             return false;
44329         }
44330         
44331         // encryption ?
44332         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44333     },
44334     
44335     drawFromImage: function(img_src)
44336     {
44337         var img = new Image();
44338         
44339         img.onload = function(){
44340             this.canvasElCtx().drawImage(img, 0, 0);
44341         }.bind(this);
44342         
44343         img.src = img_src;
44344         
44345         this.is_empty = false;
44346     },
44347     
44348     selectImage: function()
44349     {
44350         this.fileEl().dom.click();
44351     },
44352     
44353     uploadImage: function(e)
44354     {
44355         var reader = new FileReader();
44356         
44357         reader.onload = function(e){
44358             var img = new Image();
44359             img.onload = function(){
44360                 this.reset();
44361                 this.canvasElCtx().drawImage(img, 0, 0);
44362             }.bind(this);
44363             img.src = e.target.result;
44364         }.bind(this);
44365         
44366         reader.readAsDataURL(e.target.files[0]);
44367     },
44368     
44369     // Bezier Point Constructor
44370     Point: (function () {
44371         function Point(x, y, time) {
44372             this.x = x;
44373             this.y = y;
44374             this.time = time || Date.now();
44375         }
44376         Point.prototype.distanceTo = function (start) {
44377             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44378         };
44379         Point.prototype.equals = function (other) {
44380             return this.x === other.x && this.y === other.y && this.time === other.time;
44381         };
44382         Point.prototype.velocityFrom = function (start) {
44383             return this.time !== start.time
44384             ? this.distanceTo(start) / (this.time - start.time)
44385             : 0;
44386         };
44387         return Point;
44388     }()),
44389     
44390     
44391     // Bezier Constructor
44392     Bezier: (function () {
44393         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44394             this.startPoint = startPoint;
44395             this.control2 = control2;
44396             this.control1 = control1;
44397             this.endPoint = endPoint;
44398             this.startWidth = startWidth;
44399             this.endWidth = endWidth;
44400         }
44401         Bezier.fromPoints = function (points, widths, scope) {
44402             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44403             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44404             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44405         };
44406         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44407             var dx1 = s1.x - s2.x;
44408             var dy1 = s1.y - s2.y;
44409             var dx2 = s2.x - s3.x;
44410             var dy2 = s2.y - s3.y;
44411             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44412             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44413             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44414             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44415             var dxm = m1.x - m2.x;
44416             var dym = m1.y - m2.y;
44417             var k = l2 / (l1 + l2);
44418             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44419             var tx = s2.x - cm.x;
44420             var ty = s2.y - cm.y;
44421             return {
44422                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44423                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44424             };
44425         };
44426         Bezier.prototype.length = function () {
44427             var steps = 10;
44428             var length = 0;
44429             var px;
44430             var py;
44431             for (var i = 0; i <= steps; i += 1) {
44432                 var t = i / steps;
44433                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44434                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44435                 if (i > 0) {
44436                     var xdiff = cx - px;
44437                     var ydiff = cy - py;
44438                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44439                 }
44440                 px = cx;
44441                 py = cy;
44442             }
44443             return length;
44444         };
44445         Bezier.prototype.point = function (t, start, c1, c2, end) {
44446             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44447             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44448             + (3.0 * c2 * (1.0 - t) * t * t)
44449             + (end * t * t * t);
44450         };
44451         return Bezier;
44452     }()),
44453     
44454     throttleStroke: function(fn, wait) {
44455       if (wait === void 0) { wait = 250; }
44456       var previous = 0;
44457       var timeout = null;
44458       var result;
44459       var storedContext;
44460       var storedArgs;
44461       var later = function () {
44462           previous = Date.now();
44463           timeout = null;
44464           result = fn.apply(storedContext, storedArgs);
44465           if (!timeout) {
44466               storedContext = null;
44467               storedArgs = [];
44468           }
44469       };
44470       return function wrapper() {
44471           var args = [];
44472           for (var _i = 0; _i < arguments.length; _i++) {
44473               args[_i] = arguments[_i];
44474           }
44475           var now = Date.now();
44476           var remaining = wait - (now - previous);
44477           storedContext = this;
44478           storedArgs = args;
44479           if (remaining <= 0 || remaining > wait) {
44480               if (timeout) {
44481                   clearTimeout(timeout);
44482                   timeout = null;
44483               }
44484               previous = now;
44485               result = fn.apply(storedContext, storedArgs);
44486               if (!timeout) {
44487                   storedContext = null;
44488                   storedArgs = [];
44489               }
44490           }
44491           else if (!timeout) {
44492               timeout = window.setTimeout(later, remaining);
44493           }
44494           return result;
44495       };
44496   }
44497   
44498 });
44499
44500  
44501
44502